go.mod: bump client-go and api machinerie
I had to run `make generate`. Some API functions got additional parameters `Options` and `Context`. I used empty options and `context.TODO()` for now. Signed-off-by: leonnicolas <leonloechner@gmx.de>
This commit is contained in:
		
							
								
								
									
										103
									
								
								vendor/k8s.io/client-go/transport/cache.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										103
									
								
								vendor/k8s.io/client-go/transport/cache.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -20,10 +20,12 @@ import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	utilnet "k8s.io/apimachinery/pkg/util/net" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| ) | ||||
|  | ||||
| // TlsTransportCache caches TLS http.RoundTrippers different configurations. The | ||||
| @@ -39,13 +41,15 @@ const idleConnsPerHost = 25 | ||||
| var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)} | ||||
|  | ||||
| type tlsCacheKey struct { | ||||
| 	insecure   bool | ||||
| 	caData     string | ||||
| 	certData   string | ||||
| 	keyData    string | ||||
| 	getCert    string | ||||
| 	serverName string | ||||
| 	dial       string | ||||
| 	insecure           bool | ||||
| 	caData             string | ||||
| 	certData           string | ||||
| 	keyData            string `datapolicy:"security-key"` | ||||
| 	certFile           string | ||||
| 	keyFile            string | ||||
| 	serverName         string | ||||
| 	nextProtos         string | ||||
| 	disableCompression bool | ||||
| } | ||||
|  | ||||
| func (t tlsCacheKey) String() string { | ||||
| @@ -53,22 +57,24 @@ func (t tlsCacheKey) String() string { | ||||
| 	if len(t.keyData) > 0 { | ||||
| 		keyText = "<redacted>" | ||||
| 	} | ||||
| 	return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial) | ||||
| 	return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression) | ||||
| } | ||||
|  | ||||
| func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { | ||||
| 	key, err := tlsConfigKey(config) | ||||
| 	key, canCache, err := tlsConfigKey(config) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Ensure we only create a single transport for the given TLS options | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	if canCache { | ||||
| 		// Ensure we only create a single transport for the given TLS options | ||||
| 		c.mu.Lock() | ||||
| 		defer c.mu.Unlock() | ||||
|  | ||||
| 	// See if we already have a custom transport for this config | ||||
| 	if t, ok := c.transports[key]; ok { | ||||
| 		return t, nil | ||||
| 		// See if we already have a custom transport for this config | ||||
| 		if t, ok := c.transports[key]; ok { | ||||
| 			return t, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the TLS options for this client config | ||||
| @@ -77,7 +83,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// The options didn't require a custom TLS config | ||||
| 	if tlsConfig == nil && config.Dial == nil { | ||||
| 	if tlsConfig == nil && config.Dial == nil && config.Proxy == nil { | ||||
| 		return http.DefaultTransport, nil | ||||
| 	} | ||||
|  | ||||
| @@ -88,30 +94,65 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { | ||||
| 			KeepAlive: 30 * time.Second, | ||||
| 		}).DialContext | ||||
| 	} | ||||
| 	// Cache a single transport for these options | ||||
| 	c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{ | ||||
| 		Proxy:               http.ProxyFromEnvironment, | ||||
|  | ||||
| 	// 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 { | ||||
| 		dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial) | ||||
| 		tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate | ||||
| 		dial = dynamicCertDialer.connDialer.DialContext | ||||
| 		go dynamicCertDialer.Run(wait.NeverStop) | ||||
| 	} | ||||
|  | ||||
| 	proxy := http.ProxyFromEnvironment | ||||
| 	if config.Proxy != nil { | ||||
| 		proxy = config.Proxy | ||||
| 	} | ||||
|  | ||||
| 	transport := utilnet.SetTransportDefaults(&http.Transport{ | ||||
| 		Proxy:               proxy, | ||||
| 		TLSHandshakeTimeout: 10 * time.Second, | ||||
| 		TLSClientConfig:     tlsConfig, | ||||
| 		MaxIdleConnsPerHost: idleConnsPerHost, | ||||
| 		DialContext:         dial, | ||||
| 		DisableCompression:  config.DisableCompression, | ||||
| 	}) | ||||
| 	return c.transports[key], nil | ||||
|  | ||||
| 	if canCache { | ||||
| 		// Cache a single transport for these options | ||||
| 		c.transports[key] = transport | ||||
| 	} | ||||
|  | ||||
| 	return transport, nil | ||||
| } | ||||
|  | ||||
| // tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor | ||||
| func tlsConfigKey(c *Config) (tlsCacheKey, error) { | ||||
| func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) { | ||||
| 	// Make sure ca/key/cert content is loaded | ||||
| 	if err := loadTLSFiles(c); err != nil { | ||||
| 		return tlsCacheKey{}, err | ||||
| 		return tlsCacheKey{}, false, err | ||||
| 	} | ||||
| 	return tlsCacheKey{ | ||||
| 		insecure:   c.TLS.Insecure, | ||||
| 		caData:     string(c.TLS.CAData), | ||||
| 		certData:   string(c.TLS.CertData), | ||||
| 		keyData:    string(c.TLS.KeyData), | ||||
| 		getCert:    fmt.Sprintf("%p", c.TLS.GetCert), | ||||
| 		serverName: c.TLS.ServerName, | ||||
| 		dial:       fmt.Sprintf("%p", c.Dial), | ||||
| 	}, nil | ||||
|  | ||||
| 	if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil { | ||||
| 		// cannot determine equality for functions | ||||
| 		return tlsCacheKey{}, false, nil | ||||
| 	} | ||||
|  | ||||
| 	k := tlsCacheKey{ | ||||
| 		insecure:           c.TLS.Insecure, | ||||
| 		caData:             string(c.TLS.CAData), | ||||
| 		serverName:         c.TLS.ServerName, | ||||
| 		nextProtos:         strings.Join(c.TLS.NextProtos, ","), | ||||
| 		disableCompression: c.DisableCompression, | ||||
| 	} | ||||
|  | ||||
| 	if c.TLS.ReloadTLSFiles { | ||||
| 		k.certFile = c.TLS.CertFile | ||||
| 		k.keyFile = c.TLS.KeyFile | ||||
| 	} else { | ||||
| 		k.certData = string(c.TLS.CertData) | ||||
| 		k.keyData = string(c.TLS.KeyData) | ||||
| 	} | ||||
|  | ||||
| 	return k, true, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										176
									
								
								vendor/k8s.io/client-go/transport/cert_rotation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								vendor/k8s.io/client-go/transport/cert_rotation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| /* | ||||
| Copyright 2020 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package transport | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	utilnet "k8s.io/apimachinery/pkg/util/net" | ||||
| 	utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/wait" | ||||
| 	"k8s.io/client-go/util/connrotation" | ||||
| 	"k8s.io/client-go/util/workqueue" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| const workItemKey = "key" | ||||
|  | ||||
| // CertCallbackRefreshDuration is exposed so that integration tests can crank up the reload speed. | ||||
| var CertCallbackRefreshDuration = 5 * time.Minute | ||||
|  | ||||
| type reloadFunc func(*tls.CertificateRequestInfo) (*tls.Certificate, error) | ||||
|  | ||||
| type dynamicClientCert struct { | ||||
| 	clientCert *tls.Certificate | ||||
| 	certMtx    sync.RWMutex | ||||
|  | ||||
| 	reload     reloadFunc | ||||
| 	connDialer *connrotation.Dialer | ||||
|  | ||||
| 	// queue only ever has one item, but it has nice error handling backoff/retry semantics | ||||
| 	queue workqueue.RateLimitingInterface | ||||
| } | ||||
|  | ||||
| func certRotatingDialer(reload reloadFunc, dial utilnet.DialFunc) *dynamicClientCert { | ||||
| 	d := &dynamicClientCert{ | ||||
| 		reload:     reload, | ||||
| 		connDialer: connrotation.NewDialer(connrotation.DialFunc(dial)), | ||||
| 		queue:      workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "DynamicClientCertificate"), | ||||
| 	} | ||||
|  | ||||
| 	return d | ||||
| } | ||||
|  | ||||
| // loadClientCert calls the callback and rotates connections if needed | ||||
| func (c *dynamicClientCert) loadClientCert() (*tls.Certificate, error) { | ||||
| 	cert, err := c.reload(nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// check to see if we have a change. If the values are the same, do nothing. | ||||
| 	c.certMtx.RLock() | ||||
| 	haveCert := c.clientCert != nil | ||||
| 	if certsEqual(c.clientCert, cert) { | ||||
| 		c.certMtx.RUnlock() | ||||
| 		return c.clientCert, nil | ||||
| 	} | ||||
| 	c.certMtx.RUnlock() | ||||
|  | ||||
| 	c.certMtx.Lock() | ||||
| 	c.clientCert = cert | ||||
| 	c.certMtx.Unlock() | ||||
|  | ||||
| 	// The first certificate requested is not a rotation that is worth closing connections for | ||||
| 	if !haveCert { | ||||
| 		return cert, nil | ||||
| 	} | ||||
|  | ||||
| 	klog.V(1).Infof("certificate rotation detected, shutting down client connections to start using new credentials") | ||||
| 	c.connDialer.CloseAll() | ||||
|  | ||||
| 	return cert, nil | ||||
| } | ||||
|  | ||||
| // certsEqual compares tls Certificates, ignoring the Leaf which may get filled in dynamically | ||||
| func certsEqual(left, right *tls.Certificate) bool { | ||||
| 	if left == nil || right == nil { | ||||
| 		return left == right | ||||
| 	} | ||||
|  | ||||
| 	if !byteMatrixEqual(left.Certificate, right.Certificate) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(left.PrivateKey, right.PrivateKey) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if !byteMatrixEqual(left.SignedCertificateTimestamps, right.SignedCertificateTimestamps) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if !bytes.Equal(left.OCSPStaple, right.OCSPStaple) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func byteMatrixEqual(left, right [][]byte) bool { | ||||
| 	if len(left) != len(right) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	for i := range left { | ||||
| 		if !bytes.Equal(left[i], right[i]) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // run starts the controller and blocks until stopCh is closed. | ||||
| func (c *dynamicClientCert) Run(stopCh <-chan struct{}) { | ||||
| 	defer utilruntime.HandleCrash() | ||||
| 	defer c.queue.ShutDown() | ||||
|  | ||||
| 	klog.V(3).Infof("Starting client certificate rotation controller") | ||||
| 	defer klog.V(3).Infof("Shutting down client certificate rotation controller") | ||||
|  | ||||
| 	go wait.Until(c.runWorker, time.Second, stopCh) | ||||
|  | ||||
| 	go wait.PollImmediateUntil(CertCallbackRefreshDuration, func() (bool, error) { | ||||
| 		c.queue.Add(workItemKey) | ||||
| 		return false, nil | ||||
| 	}, stopCh) | ||||
|  | ||||
| 	<-stopCh | ||||
| } | ||||
|  | ||||
| func (c *dynamicClientCert) runWorker() { | ||||
| 	for c.processNextWorkItem() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *dynamicClientCert) processNextWorkItem() bool { | ||||
| 	dsKey, quit := c.queue.Get() | ||||
| 	if quit { | ||||
| 		return false | ||||
| 	} | ||||
| 	defer c.queue.Done(dsKey) | ||||
|  | ||||
| 	_, err := c.loadClientCert() | ||||
| 	if err == nil { | ||||
| 		c.queue.Forget(dsKey) | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err)) | ||||
| 	c.queue.AddRateLimited(dsKey) | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (c *dynamicClientCert) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { | ||||
| 	return c.loadClientCert() | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/k8s.io/client-go/transport/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/k8s.io/client-go/transport/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -21,6 +21,7 @@ import ( | ||||
| 	"crypto/tls" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| ) | ||||
|  | ||||
| // Config holds various options for establishing a transport. | ||||
| @@ -34,10 +35,10 @@ type Config struct { | ||||
|  | ||||
| 	// Username and password for basic authentication | ||||
| 	Username string | ||||
| 	Password string | ||||
| 	Password string `datapolicy:"password"` | ||||
|  | ||||
| 	// Bearer token for authentication | ||||
| 	BearerToken string | ||||
| 	BearerToken string `datapolicy:"token"` | ||||
|  | ||||
| 	// Path to a file containing a BearerToken. | ||||
| 	// If set, the contents are periodically read. | ||||
| @@ -47,6 +48,10 @@ type Config struct { | ||||
| 	// Impersonate is the config that this Config will impersonate using | ||||
| 	Impersonate ImpersonationConfig | ||||
|  | ||||
| 	// DisableCompression bypasses automatic GZip compression requests to the | ||||
| 	// server. | ||||
| 	DisableCompression bool | ||||
|  | ||||
| 	// Transport may be used for custom HTTP behavior. This attribute may | ||||
| 	// not be specified with the TLS client certificate options. Use | ||||
| 	// WrapTransport for most client level operations. | ||||
| @@ -64,6 +69,13 @@ type Config struct { | ||||
|  | ||||
| 	// Dial specifies the dial function for creating unencrypted TCP connections. | ||||
| 	Dial func(ctx context.Context, network, address string) (net.Conn, error) | ||||
|  | ||||
| 	// Proxy is the proxy func to be used for all requests made by this | ||||
| 	// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy | ||||
| 	// returns a nil *URL, no proxy is used. | ||||
| 	// | ||||
| 	// socks5 proxying does not currently support spdy streaming endpoints. | ||||
| 	Proxy func(*http.Request) (*url.URL, error) | ||||
| } | ||||
|  | ||||
| // ImpersonationConfig has all the available impersonation options | ||||
| @@ -96,7 +108,7 @@ func (c *Config) HasCertAuth() bool { | ||||
| 	return (len(c.TLS.CertData) != 0 || len(c.TLS.CertFile) != 0) && (len(c.TLS.KeyData) != 0 || len(c.TLS.KeyFile) != 0) | ||||
| } | ||||
|  | ||||
| // HasCertCallbacks returns whether the configuration has certificate callback or not. | ||||
| // HasCertCallback returns whether the configuration has certificate callback or not. | ||||
| func (c *Config) HasCertCallback() bool { | ||||
| 	return c.TLS.GetCert != nil | ||||
| } | ||||
| @@ -111,9 +123,10 @@ func (c *Config) Wrap(fn WrapperFunc) { | ||||
|  | ||||
| // TLSConfig holds the information needed to set up a TLS transport. | ||||
| type TLSConfig struct { | ||||
| 	CAFile   string // Path of the PEM-encoded server trusted root certificates. | ||||
| 	CertFile string // Path of the PEM-encoded client certificate. | ||||
| 	KeyFile  string // Path of the PEM-encoded client key. | ||||
| 	CAFile         string // Path of the PEM-encoded server trusted root certificates. | ||||
| 	CertFile       string // Path of the PEM-encoded client certificate. | ||||
| 	KeyFile        string // Path of the PEM-encoded client key. | ||||
| 	ReloadTLSFiles bool   // Set to indicate that the original config provided files, and that they should be reloaded | ||||
|  | ||||
| 	Insecure   bool   // Server should be accessed without verifying the certificate. For testing only. | ||||
| 	ServerName string // Override for the server name passed to the server for SNI and used to verify certificates. | ||||
| @@ -122,5 +135,11 @@ type TLSConfig struct { | ||||
| 	CertData []byte // Bytes of the PEM-encoded client certificate. Supercedes CertFile. | ||||
| 	KeyData  []byte // Bytes of the PEM-encoded client key. Supercedes KeyFile. | ||||
|  | ||||
| 	// NextProtos is a list of supported application level protocols, in order of preference. | ||||
| 	// Used to populate tls.Config.NextProtos. | ||||
| 	// To indicate to the server http/1.1 is preferred over http/2, set to ["http/1.1", "h2"] (though the server is free to ignore that preference). | ||||
| 	// To use only http/1.1, set to ["http/1.1"]. | ||||
| 	NextProtos []string | ||||
|  | ||||
| 	GetCert func() (*tls.Certificate, error) // Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field. | ||||
| } | ||||
|   | ||||
							
								
								
									
										146
									
								
								vendor/k8s.io/client-go/transport/round_trippers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								vendor/k8s.io/client-go/transport/round_trippers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -23,9 +23,9 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/oauth2" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	utilnet "k8s.io/apimachinery/pkg/util/net" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| // HTTPWrappersForConfig wraps a round tripper with any relevant layered | ||||
| @@ -67,23 +67,19 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip | ||||
| // DebugWrappers wraps a round tripper and logs based on the current log level. | ||||
| func DebugWrappers(rt http.RoundTripper) http.RoundTripper { | ||||
| 	switch { | ||||
| 	case bool(klog.V(9)): | ||||
| 		rt = newDebuggingRoundTripper(rt, debugCurlCommand, debugURLTiming, debugResponseHeaders) | ||||
| 	case bool(klog.V(8)): | ||||
| 		rt = newDebuggingRoundTripper(rt, debugJustURL, debugRequestHeaders, debugResponseStatus, debugResponseHeaders) | ||||
| 	case bool(klog.V(7)): | ||||
| 		rt = newDebuggingRoundTripper(rt, debugJustURL, debugRequestHeaders, debugResponseStatus) | ||||
| 	case bool(klog.V(6)): | ||||
| 		rt = newDebuggingRoundTripper(rt, debugURLTiming) | ||||
| 	case bool(klog.V(9).Enabled()): | ||||
| 		rt = NewDebuggingRoundTripper(rt, DebugCurlCommand, DebugURLTiming, DebugResponseHeaders) | ||||
| 	case bool(klog.V(8).Enabled()): | ||||
| 		rt = NewDebuggingRoundTripper(rt, DebugJustURL, DebugRequestHeaders, DebugResponseStatus, DebugResponseHeaders) | ||||
| 	case bool(klog.V(7).Enabled()): | ||||
| 		rt = NewDebuggingRoundTripper(rt, DebugJustURL, DebugRequestHeaders, DebugResponseStatus) | ||||
| 	case bool(klog.V(6).Enabled()): | ||||
| 		rt = NewDebuggingRoundTripper(rt, DebugURLTiming) | ||||
| 	} | ||||
|  | ||||
| 	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 } | ||||
| @@ -154,6 +146,7 @@ type userAgentRoundTripper struct { | ||||
| 	rt    http.RoundTripper | ||||
| } | ||||
|  | ||||
| // NewUserAgentRoundTripper will add User-Agent header to a request unless it has already been set. | ||||
| func NewUserAgentRoundTripper(agent string, rt http.RoundTripper) http.RoundTripper { | ||||
| 	return &userAgentRoundTripper{agent, rt} | ||||
| } | ||||
| @@ -168,18 +161,14 @@ 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 } | ||||
|  | ||||
| type basicAuthRoundTripper struct { | ||||
| 	username string | ||||
| 	password string | ||||
| 	password string `datapolicy:"password"` | ||||
| 	rt       http.RoundTripper | ||||
| } | ||||
|  | ||||
| @@ -199,11 +188,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 +244,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 } | ||||
| @@ -280,7 +261,7 @@ func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTr | ||||
| 	return &bearerAuthRoundTripper{bearer, nil, rt} | ||||
| } | ||||
|  | ||||
| // NewBearerAuthRoundTripper adds the provided bearer token to a request | ||||
| // NewBearerAuthWithRefreshRoundTripper adds the provided bearer token to a request | ||||
| // unless the authorization header has already been set. | ||||
| // If tokenFile is non-empty, it is periodically read, | ||||
| // and the last successfully read content is used as the bearer token. | ||||
| @@ -318,18 +299,14 @@ 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 } | ||||
|  | ||||
| // requestInfo keeps track of information about a request/response combination | ||||
| type requestInfo struct { | ||||
| 	RequestHeaders http.Header | ||||
| 	RequestHeaders http.Header `datapolicy:"token"` | ||||
| 	RequestVerb    string | ||||
| 	RequestURL     string | ||||
|  | ||||
| @@ -364,6 +341,7 @@ func (r *requestInfo) toCurl() string { | ||||
| 	headers := "" | ||||
| 	for key, values := range r.RequestHeaders { | ||||
| 		for _, value := range values { | ||||
| 			value = maskValue(key, value) | ||||
| 			headers += fmt.Sprintf(` -H %q`, fmt.Sprintf("%s: %s", key, value)) | ||||
| 		} | ||||
| 	} | ||||
| @@ -375,25 +353,35 @@ func (r *requestInfo) toCurl() string { | ||||
| // through it based on what is configured | ||||
| type debuggingRoundTripper struct { | ||||
| 	delegatedRoundTripper http.RoundTripper | ||||
|  | ||||
| 	levels map[debugLevel]bool | ||||
| 	levels                map[DebugLevel]bool | ||||
| } | ||||
|  | ||||
| type debugLevel int | ||||
| // DebugLevel is used to enable debugging of certain | ||||
| // HTTP requests and responses fields via the debuggingRoundTripper. | ||||
| type DebugLevel int | ||||
|  | ||||
| const ( | ||||
| 	debugJustURL debugLevel = iota | ||||
| 	debugURLTiming | ||||
| 	debugCurlCommand | ||||
| 	debugRequestHeaders | ||||
| 	debugResponseStatus | ||||
| 	debugResponseHeaders | ||||
| 	// DebugJustURL will add to the debug output HTTP requests method and url. | ||||
| 	DebugJustURL DebugLevel = iota | ||||
| 	// DebugURLTiming will add to the debug output the duration of HTTP requests. | ||||
| 	DebugURLTiming | ||||
| 	// DebugCurlCommand will add to the debug output the curl command equivalent to the | ||||
| 	// HTTP request. | ||||
| 	DebugCurlCommand | ||||
| 	// DebugRequestHeaders will add to the debug output the HTTP requests headers. | ||||
| 	DebugRequestHeaders | ||||
| 	// DebugResponseStatus will add to the debug output the HTTP response status. | ||||
| 	DebugResponseStatus | ||||
| 	// DebugResponseHeaders will add to the debug output the HTTP response headers. | ||||
| 	DebugResponseHeaders | ||||
| ) | ||||
|  | ||||
| func newDebuggingRoundTripper(rt http.RoundTripper, levels ...debugLevel) *debuggingRoundTripper { | ||||
| // NewDebuggingRoundTripper allows to display in the logs output debug information | ||||
| // on the API requests performed by the client. | ||||
| func NewDebuggingRoundTripper(rt http.RoundTripper, levels ...DebugLevel) http.RoundTripper { | ||||
| 	drt := &debuggingRoundTripper{ | ||||
| 		delegatedRoundTripper: rt, | ||||
| 		levels:                make(map[debugLevel]bool, len(levels)), | ||||
| 		levels:                make(map[DebugLevel]bool, len(levels)), | ||||
| 	} | ||||
| 	for _, v := range levels { | ||||
| 		drt.levels[v] = true | ||||
| @@ -402,27 +390,55 @@ 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{ | ||||
| 	"bearer":    true, | ||||
| 	"basic":     true, | ||||
| 	"negotiate": true, | ||||
| } | ||||
|  | ||||
| // maskValue masks credential content from authorization headers | ||||
| // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization | ||||
| func maskValue(key string, value string) string { | ||||
| 	if !strings.EqualFold(key, "Authorization") { | ||||
| 		return value | ||||
| 	} | ||||
| 	if len(value) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	var authType string | ||||
| 	if i := strings.Index(value, " "); i > 0 { | ||||
| 		authType = value[0:i] | ||||
| 	} else { | ||||
| 		authType = value | ||||
| 	} | ||||
| 	if !knownAuthTypes[strings.ToLower(authType)] { | ||||
| 		return "<masked>" | ||||
| 	} | ||||
| 	if len(value) > len(authType)+1 { | ||||
| 		value = authType + " <masked>" | ||||
| 	} else { | ||||
| 		value = authType | ||||
| 	} | ||||
| 	return value | ||||
| } | ||||
|  | ||||
| func (rt *debuggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | ||||
| 	reqInfo := newRequestInfo(req) | ||||
|  | ||||
| 	if rt.levels[debugJustURL] { | ||||
| 	if rt.levels[DebugJustURL] { | ||||
| 		klog.Infof("%s %s", reqInfo.RequestVerb, reqInfo.RequestURL) | ||||
| 	} | ||||
| 	if rt.levels[debugCurlCommand] { | ||||
| 	if rt.levels[DebugCurlCommand] { | ||||
| 		klog.Infof("%s", reqInfo.toCurl()) | ||||
|  | ||||
| 	} | ||||
| 	if rt.levels[debugRequestHeaders] { | ||||
| 		klog.Infof("Request Headers:") | ||||
| 	if rt.levels[DebugRequestHeaders] { | ||||
| 		klog.Info("Request Headers:") | ||||
| 		for key, values := range reqInfo.RequestHeaders { | ||||
| 			for _, value := range values { | ||||
| 				value = maskValue(key, value) | ||||
| 				klog.Infof("    %s: %s", key, value) | ||||
| 			} | ||||
| 		} | ||||
| @@ -434,14 +450,14 @@ func (rt *debuggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, e | ||||
|  | ||||
| 	reqInfo.complete(response, err) | ||||
|  | ||||
| 	if rt.levels[debugURLTiming] { | ||||
| 	if rt.levels[DebugURLTiming] { | ||||
| 		klog.Infof("%s %s %s in %d milliseconds", reqInfo.RequestVerb, reqInfo.RequestURL, reqInfo.ResponseStatus, reqInfo.Duration.Nanoseconds()/int64(time.Millisecond)) | ||||
| 	} | ||||
| 	if rt.levels[debugResponseStatus] { | ||||
| 	if rt.levels[DebugResponseStatus] { | ||||
| 		klog.Infof("Response Status: %s in %d milliseconds", reqInfo.ResponseStatus, reqInfo.Duration.Nanoseconds()/int64(time.Millisecond)) | ||||
| 	} | ||||
| 	if rt.levels[debugResponseHeaders] { | ||||
| 		klog.Infof("Response Headers:") | ||||
| 	if rt.levels[DebugResponseHeaders] { | ||||
| 		klog.Info("Response Headers:") | ||||
| 		for key, values := range reqInfo.ResponseHeaders { | ||||
| 			for _, value := range values { | ||||
| 				klog.Infof("    %s: %s", key, value) | ||||
|   | ||||
							
								
								
									
										70
									
								
								vendor/k8s.io/client-go/transport/token_source.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								vendor/k8s.io/client-go/transport/token_source.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -25,7 +25,8 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/oauth2" | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| // TokenSourceWrapTransport returns a WrapTransport that injects bearer tokens | ||||
| @@ -42,9 +43,29 @@ func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) htt | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewCachedFileTokenSource returns a oauth2.TokenSource reads a token from a | ||||
| // file at a specified path and periodically reloads it. | ||||
| func NewCachedFileTokenSource(path string) oauth2.TokenSource { | ||||
| type ResettableTokenSource interface { | ||||
| 	oauth2.TokenSource | ||||
| 	ResetTokenOlderThan(time.Time) | ||||
| } | ||||
|  | ||||
| // ResettableTokenSourceWrapTransport returns a WrapTransport that injects bearer tokens | ||||
| // authentication from an ResettableTokenSource. | ||||
| func ResettableTokenSourceWrapTransport(ts ResettableTokenSource) func(http.RoundTripper) http.RoundTripper { | ||||
| 	return func(rt http.RoundTripper) http.RoundTripper { | ||||
| 		return &tokenSourceTransport{ | ||||
| 			base: rt, | ||||
| 			ort: &oauth2.Transport{ | ||||
| 				Source: ts, | ||||
| 				Base:   rt, | ||||
| 			}, | ||||
| 			src: ts, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewCachedFileTokenSource returns a resettable token source which reads a | ||||
| // token from a file at a specified path and periodically reloads it. | ||||
| func NewCachedFileTokenSource(path string) *cachingTokenSource { | ||||
| 	return &cachingTokenSource{ | ||||
| 		now:    time.Now, | ||||
| 		leeway: 10 * time.Second, | ||||
| @@ -59,9 +80,19 @@ func NewCachedFileTokenSource(path string) oauth2.TokenSource { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewCachedTokenSource returns resettable token source with caching. It reads | ||||
| // a token from a designed TokenSource if not in cache or expired. | ||||
| func NewCachedTokenSource(ts oauth2.TokenSource) *cachingTokenSource { | ||||
| 	return &cachingTokenSource{ | ||||
| 		now:  time.Now, | ||||
| 		base: ts, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type tokenSourceTransport struct { | ||||
| 	base http.RoundTripper | ||||
| 	ort  http.RoundTripper | ||||
| 	src  ResettableTokenSource | ||||
| } | ||||
|  | ||||
| func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||||
| @@ -69,7 +100,23 @@ func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, e | ||||
| 	if req.Header.Get("Authorization") != "" { | ||||
| 		return tst.base.RoundTrip(req) | ||||
| 	} | ||||
| 	return tst.ort.RoundTrip(req) | ||||
| 	// record time before RoundTrip to make sure newly acquired Unauthorized | ||||
| 	// token would not be reset. Another request from user is required to reset | ||||
| 	// and proceed. | ||||
| 	start := time.Now() | ||||
| 	resp, err := tst.ort.RoundTrip(req) | ||||
| 	if err == nil && resp != nil && resp.StatusCode == 401 && tst.src != nil { | ||||
| 		tst.src.ResetTokenOlderThan(start) | ||||
| 	} | ||||
| 	return resp, err | ||||
| } | ||||
|  | ||||
| func (tst *tokenSourceTransport) CancelRequest(req *http.Request) { | ||||
| 	if req.Header.Get("Authorization") != "" { | ||||
| 		tryCancelRequest(tst.base, req) | ||||
| 		return | ||||
| 	} | ||||
| 	tryCancelRequest(tst.ort, req) | ||||
| } | ||||
|  | ||||
| type fileTokenSource struct { | ||||
| @@ -101,13 +148,12 @@ type cachingTokenSource struct { | ||||
|  | ||||
| 	sync.RWMutex | ||||
| 	tok *oauth2.Token | ||||
| 	t   time.Time | ||||
|  | ||||
| 	// for testing | ||||
| 	now func() time.Time | ||||
| } | ||||
|  | ||||
| var _ = oauth2.TokenSource(&cachingTokenSource{}) | ||||
|  | ||||
| func (ts *cachingTokenSource) Token() (*oauth2.Token, error) { | ||||
| 	now := ts.now() | ||||
| 	// fast path | ||||
| @@ -135,6 +181,16 @@ func (ts *cachingTokenSource) Token() (*oauth2.Token, error) { | ||||
| 		return ts.tok, nil | ||||
| 	} | ||||
|  | ||||
| 	ts.t = ts.now() | ||||
| 	ts.tok = tok | ||||
| 	return tok, nil | ||||
| } | ||||
|  | ||||
| func (ts *cachingTokenSource) ResetTokenOlderThan(t time.Time) { | ||||
| 	ts.Lock() | ||||
| 	defer ts.Unlock() | ||||
| 	if ts.t.Before(t) { | ||||
| 		ts.tok = nil | ||||
| 		ts.t = time.Time{} | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										80
									
								
								vendor/k8s.io/client-go/transport/transport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/k8s.io/client-go/transport/transport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -23,6 +23,11 @@ import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	utilnet "k8s.io/apimachinery/pkg/util/net" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| // New returns an http.RoundTripper that will provide the authentication | ||||
| @@ -53,7 +58,7 @@ func New(config *Config) (http.RoundTripper, error) { | ||||
| // TLSConfigFor returns a tls.Config that will provide the transport level security defined | ||||
| // by the provided Config. Will return nil if no transport level security is requested. | ||||
| func TLSConfigFor(c *Config) (*tls.Config, error) { | ||||
| 	if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0) { | ||||
| 	if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0 || len(c.TLS.NextProtos) > 0) { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if c.HasCA() && c.TLS.Insecure { | ||||
| @@ -70,6 +75,7 @@ func TLSConfigFor(c *Config) (*tls.Config, error) { | ||||
| 		MinVersion:         tls.VersionTLS12, | ||||
| 		InsecureSkipVerify: c.TLS.Insecure, | ||||
| 		ServerName:         c.TLS.ServerName, | ||||
| 		NextProtos:         c.TLS.NextProtos, | ||||
| 	} | ||||
|  | ||||
| 	if c.HasCA() { | ||||
| @@ -77,7 +83,8 @@ func TLSConfigFor(c *Config) (*tls.Config, error) { | ||||
| 	} | ||||
|  | ||||
| 	var staticCert *tls.Certificate | ||||
| 	if c.HasCertAuth() { | ||||
| 	// Treat cert as static if either key or cert was data, not a file | ||||
| 	if c.HasCertAuth() && !c.TLS.ReloadTLSFiles { | ||||
| 		// If key/cert were provided, verify them before setting up | ||||
| 		// tlsConfig.GetClientCertificate. | ||||
| 		cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData) | ||||
| @@ -87,6 +94,11 @@ func TLSConfigFor(c *Config) (*tls.Config, error) { | ||||
| 		staticCert = &cert | ||||
| 	} | ||||
|  | ||||
| 	var dynamicCertLoader func() (*tls.Certificate, error) | ||||
| 	if c.TLS.ReloadTLSFiles { | ||||
| 		dynamicCertLoader = cachingCertificateLoader(c.TLS.CertFile, c.TLS.KeyFile) | ||||
| 	} | ||||
|  | ||||
| 	if c.HasCertAuth() || c.HasCertCallback() { | ||||
| 		tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { | ||||
| 			// Note: static key/cert data always take precedence over cert | ||||
| @@ -94,6 +106,10 @@ func TLSConfigFor(c *Config) (*tls.Config, error) { | ||||
| 			if staticCert != nil { | ||||
| 				return staticCert, nil | ||||
| 			} | ||||
| 			// key/cert files lead to ReloadTLSFiles being set - takes precedence over cert callback | ||||
| 			if dynamicCertLoader != nil { | ||||
| 				return dynamicCertLoader() | ||||
| 			} | ||||
| 			if c.HasCertCallback() { | ||||
| 				cert, err := c.TLS.GetCert() | ||||
| 				if err != nil { | ||||
| @@ -125,6 +141,11 @@ func loadTLSFiles(c *Config) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Check that we are purely loading from files | ||||
| 	if len(c.TLS.CertFile) > 0 && len(c.TLS.CertData) == 0 && len(c.TLS.KeyFile) > 0 && len(c.TLS.KeyData) == 0 { | ||||
| 		c.TLS.ReloadTLSFiles = true | ||||
| 	} | ||||
|  | ||||
| 	c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -225,3 +246,58 @@ 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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type certificateCacheEntry struct { | ||||
| 	cert  *tls.Certificate | ||||
| 	err   error | ||||
| 	birth time.Time | ||||
| } | ||||
|  | ||||
| // isStale returns true when this cache entry is too old to be usable | ||||
| func (c *certificateCacheEntry) isStale() bool { | ||||
| 	return time.Now().Sub(c.birth) > time.Second | ||||
| } | ||||
|  | ||||
| func newCertificateCacheEntry(certFile, keyFile string) certificateCacheEntry { | ||||
| 	cert, err := tls.LoadX509KeyPair(certFile, keyFile) | ||||
| 	return certificateCacheEntry{cert: &cert, err: err, birth: time.Now()} | ||||
| } | ||||
|  | ||||
| // cachingCertificateLoader ensures that we don't hammer the filesystem when opening many connections | ||||
| // the underlying cert files are read at most once every second | ||||
| func cachingCertificateLoader(certFile, keyFile string) func() (*tls.Certificate, error) { | ||||
| 	current := newCertificateCacheEntry(certFile, keyFile) | ||||
| 	var currentMtx sync.RWMutex | ||||
|  | ||||
| 	return func() (*tls.Certificate, error) { | ||||
| 		currentMtx.RLock() | ||||
| 		if current.isStale() { | ||||
| 			currentMtx.RUnlock() | ||||
|  | ||||
| 			currentMtx.Lock() | ||||
| 			defer currentMtx.Unlock() | ||||
|  | ||||
| 			if current.isStale() { | ||||
| 				current = newCertificateCacheEntry(certFile, keyFile) | ||||
| 			} | ||||
| 		} else { | ||||
| 			defer currentMtx.RUnlock() | ||||
| 		} | ||||
|  | ||||
| 		return current.cert, current.err | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user