[VOL-3678] First implementation of the BBSim-sadis-server
Change-Id: I5077a8f861f4cc6af9759f31a4a415042c05eba3
diff --git a/vendor/k8s.io/client-go/transport/OWNERS b/vendor/k8s.io/client-go/transport/OWNERS
new file mode 100644
index 0000000..a521769
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/OWNERS
@@ -0,0 +1,9 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+reviewers:
+- smarterclayton
+- wojtek-t
+- deads2k
+- liggitt
+- krousey
+- caesarxuchao
diff --git a/vendor/k8s.io/client-go/transport/cache.go b/vendor/k8s.io/client-go/transport/cache.go
new file mode 100644
index 0000000..3ec4e19
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/cache.go
@@ -0,0 +1,151 @@
+/*
+Copyright 2015 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 (
+ "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
+// same RoundTripper will be returned for configs with identical TLS options If
+// the config has no custom TLS options, http.DefaultTransport is returned.
+type tlsTransportCache struct {
+ mu sync.Mutex
+ transports map[tlsCacheKey]*http.Transport
+}
+
+const idleConnsPerHost = 25
+
+var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)}
+
+type tlsCacheKey struct {
+ insecure bool
+ caData string
+ certData string
+ keyData string
+ certFile string
+ keyFile string
+ getCert string
+ serverName string
+ nextProtos string
+ dial string
+ disableCompression bool
+ proxy string
+}
+
+func (t tlsCacheKey) String() string {
+ keyText := "<none>"
+ if len(t.keyData) > 0 {
+ keyText = "<redacted>"
+ }
+ return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s disableCompression:%t, proxy: %s", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial, t.disableCompression, t.proxy)
+}
+
+func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
+ key, 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()
+
+ // 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
+ tlsConfig, err := TLSConfigFor(config)
+ if err != nil {
+ return nil, err
+ }
+ // The options didn't require a custom TLS config
+ if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
+ return http.DefaultTransport, nil
+ }
+
+ dial := config.Dial
+ if dial == nil {
+ dial = (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).DialContext
+ }
+
+ // 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
+ }
+
+ // Cache a single transport for these options
+ c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
+ Proxy: proxy,
+ TLSHandshakeTimeout: 10 * time.Second,
+ TLSClientConfig: tlsConfig,
+ MaxIdleConnsPerHost: idleConnsPerHost,
+ DialContext: dial,
+ DisableCompression: config.DisableCompression,
+ })
+ return c.transports[key], nil
+}
+
+// tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor
+func tlsConfigKey(c *Config) (tlsCacheKey, error) {
+ // Make sure ca/key/cert content is loaded
+ if err := loadTLSFiles(c); err != nil {
+ return tlsCacheKey{}, err
+ }
+ k := tlsCacheKey{
+ insecure: c.TLS.Insecure,
+ caData: string(c.TLS.CAData),
+ getCert: fmt.Sprintf("%p", c.TLS.GetCert),
+ serverName: c.TLS.ServerName,
+ nextProtos: strings.Join(c.TLS.NextProtos, ","),
+ dial: fmt.Sprintf("%p", c.Dial),
+ disableCompression: c.DisableCompression,
+ proxy: fmt.Sprintf("%p", c.Proxy),
+ }
+
+ 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, nil
+}
diff --git a/vendor/k8s.io/client-go/transport/cert_rotation.go b/vendor/k8s.io/client-go/transport/cert_rotation.go
new file mode 100644
index 0000000..dc22b6e
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/cert_rotation.go
@@ -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()
+}
diff --git a/vendor/k8s.io/client-go/transport/config.go b/vendor/k8s.io/client-go/transport/config.go
new file mode 100644
index 0000000..45db248
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/config.go
@@ -0,0 +1,145 @@
+/*
+Copyright 2015 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 (
+ "context"
+ "crypto/tls"
+ "net"
+ "net/http"
+ "net/url"
+)
+
+// Config holds various options for establishing a transport.
+type Config struct {
+ // UserAgent is an optional field that specifies the caller of this
+ // request.
+ UserAgent string
+
+ // The base TLS configuration for this transport.
+ TLS TLSConfig
+
+ // Username and password for basic authentication
+ Username string
+ Password string
+
+ // Bearer token for authentication
+ BearerToken string
+
+ // Path to a file containing a BearerToken.
+ // If set, the contents are periodically read.
+ // The last successfully read value takes precedence over BearerToken.
+ BearerTokenFile string
+
+ // 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.
+ Transport http.RoundTripper
+
+ // WrapTransport will be invoked for custom HTTP behavior after the
+ // underlying transport is initialized (either the transport created
+ // from TLSClientConfig, Transport, or http.DefaultTransport). The
+ // config may layer other RoundTrippers on top of the returned
+ // RoundTripper.
+ //
+ // A future release will change this field to an array. Use config.Wrap()
+ // instead of setting this value directly.
+ WrapTransport WrapperFunc
+
+ // Dial specifies the dial function for creating unencrypted TCP connections.
+ Dial func(ctx context.Context, network, address string) (net.Conn, error)
+
+ // Proxy is the 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
+type ImpersonationConfig struct {
+ // UserName matches user.Info.GetName()
+ UserName string
+ // Groups matches user.Info.GetGroups()
+ Groups []string
+ // Extra matches user.Info.GetExtra()
+ Extra map[string][]string
+}
+
+// HasCA returns whether the configuration has a certificate authority or not.
+func (c *Config) HasCA() bool {
+ return len(c.TLS.CAData) > 0 || len(c.TLS.CAFile) > 0
+}
+
+// HasBasicAuth returns whether the configuration has basic authentication or not.
+func (c *Config) HasBasicAuth() bool {
+ return len(c.Username) != 0
+}
+
+// HasTokenAuth returns whether the configuration has token authentication or not.
+func (c *Config) HasTokenAuth() bool {
+ return len(c.BearerToken) != 0 || len(c.BearerTokenFile) != 0
+}
+
+// HasCertAuth returns whether the configuration has certificate authentication or not.
+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.
+func (c *Config) HasCertCallback() bool {
+ return c.TLS.GetCert != nil
+}
+
+// Wrap adds a transport middleware function that will give the caller
+// an opportunity to wrap the underlying http.RoundTripper prior to the
+// first API call being made. The provided function is invoked after any
+// existing transport wrappers are invoked.
+func (c *Config) Wrap(fn WrapperFunc) {
+ c.WrapTransport = Wrappers(c.WrapTransport, fn)
+}
+
+// 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.
+ 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.
+
+ CAData []byte // Bytes of the PEM-encoded server trusted root certificates. Supercedes CAFile.
+ 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.
+}
diff --git a/vendor/k8s.io/client-go/transport/round_trippers.go b/vendor/k8s.io/client-go/transport/round_trippers.go
new file mode 100644
index 0000000..a05208d
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/round_trippers.go
@@ -0,0 +1,569 @@
+/*
+Copyright 2015 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 (
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+
+ "golang.org/x/oauth2"
+ "k8s.io/klog/v2"
+
+ utilnet "k8s.io/apimachinery/pkg/util/net"
+)
+
+// HTTPWrappersForConfig wraps a round tripper with any relevant layered
+// behavior from the config. Exposed to allow more clients that need HTTP-like
+// behavior but then must hijack the underlying connection (like WebSocket or
+// HTTP2 clients). Pure HTTP clients should use the RoundTripper returned from
+// New.
+func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
+ if config.WrapTransport != nil {
+ rt = config.WrapTransport(rt)
+ }
+
+ rt = DebugWrappers(rt)
+
+ // Set authentication wrappers
+ switch {
+ case config.HasBasicAuth() && config.HasTokenAuth():
+ return nil, fmt.Errorf("username/password or bearer token may be set, but not both")
+ case config.HasTokenAuth():
+ var err error
+ rt, err = NewBearerAuthWithRefreshRoundTripper(config.BearerToken, config.BearerTokenFile, rt)
+ if err != nil {
+ return nil, err
+ }
+ case config.HasBasicAuth():
+ rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
+ }
+ if len(config.UserAgent) > 0 {
+ rt = NewUserAgentRoundTripper(config.UserAgent, rt)
+ }
+ if len(config.Impersonate.UserName) > 0 ||
+ len(config.Impersonate.Groups) > 0 ||
+ len(config.Impersonate.Extra) > 0 {
+ rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
+ }
+ return rt, nil
+}
+
+// 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).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 authProxyRoundTripper struct {
+ username string
+ groups []string
+ extra map[string][]string
+
+ rt http.RoundTripper
+}
+
+// NewAuthProxyRoundTripper provides a roundtripper which will add auth proxy fields to requests for
+// authentication terminating proxy cases
+// assuming you pull the user from the context:
+// username is the user.Info.GetName() of the user
+// groups is the user.Info.GetGroups() of the user
+// extra is the user.Info.GetExtra() of the user
+// extra can contain any additional information that the authenticator
+// thought was interesting, for example authorization scopes.
+// In order to faithfully round-trip through an impersonation flow, these keys
+// MUST be lowercase.
+func NewAuthProxyRoundTripper(username string, groups []string, extra map[string][]string, rt http.RoundTripper) http.RoundTripper {
+ return &authProxyRoundTripper{
+ username: username,
+ groups: groups,
+ extra: extra,
+ rt: rt,
+ }
+}
+
+func (rt *authProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ req = utilnet.CloneRequest(req)
+ SetAuthProxyHeaders(req, rt.username, rt.groups, rt.extra)
+
+ return rt.rt.RoundTrip(req)
+}
+
+// SetAuthProxyHeaders stomps the auth proxy header fields. It mutates its argument.
+func SetAuthProxyHeaders(req *http.Request, username string, groups []string, extra map[string][]string) {
+ req.Header.Del("X-Remote-User")
+ req.Header.Del("X-Remote-Group")
+ for key := range req.Header {
+ if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
+ req.Header.Del(key)
+ }
+ }
+
+ req.Header.Set("X-Remote-User", username)
+ for _, group := range groups {
+ req.Header.Add("X-Remote-Group", group)
+ }
+ for key, values := range extra {
+ for _, value := range values {
+ req.Header.Add("X-Remote-Extra-"+headerKeyEscape(key), value)
+ }
+ }
+}
+
+func (rt *authProxyRoundTripper) CancelRequest(req *http.Request) {
+ tryCancelRequest(rt.WrappedRoundTripper(), req)
+}
+
+func (rt *authProxyRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
+
+type userAgentRoundTripper struct {
+ agent string
+ rt http.RoundTripper
+}
+
+func NewUserAgentRoundTripper(agent string, rt http.RoundTripper) http.RoundTripper {
+ return &userAgentRoundTripper{agent, rt}
+}
+
+func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ if len(req.Header.Get("User-Agent")) != 0 {
+ return rt.rt.RoundTrip(req)
+ }
+ req = utilnet.CloneRequest(req)
+ req.Header.Set("User-Agent", rt.agent)
+ return rt.rt.RoundTrip(req)
+}
+
+func (rt *userAgentRoundTripper) CancelRequest(req *http.Request) {
+ tryCancelRequest(rt.WrappedRoundTripper(), req)
+}
+
+func (rt *userAgentRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
+
+type basicAuthRoundTripper struct {
+ username string
+ password string
+ rt http.RoundTripper
+}
+
+// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a
+// request unless it has already been set.
+func NewBasicAuthRoundTripper(username, password string, rt http.RoundTripper) http.RoundTripper {
+ return &basicAuthRoundTripper{username, password, rt}
+}
+
+func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ if len(req.Header.Get("Authorization")) != 0 {
+ return rt.rt.RoundTrip(req)
+ }
+ req = utilnet.CloneRequest(req)
+ req.SetBasicAuth(rt.username, rt.password)
+ return rt.rt.RoundTrip(req)
+}
+
+func (rt *basicAuthRoundTripper) CancelRequest(req *http.Request) {
+ tryCancelRequest(rt.WrappedRoundTripper(), req)
+}
+
+func (rt *basicAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
+
+// These correspond to the headers used in pkg/apis/authentication. We don't want the package dependency,
+// but you must not change the values.
+const (
+ // ImpersonateUserHeader is used to impersonate a particular user during an API server request
+ ImpersonateUserHeader = "Impersonate-User"
+
+ // ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
+ // It can be repeated multiplied times for multiple groups.
+ ImpersonateGroupHeader = "Impersonate-Group"
+
+ // ImpersonateUserExtraHeaderPrefix is a prefix for a header used to impersonate an entry in the
+ // extra map[string][]string for user.Info. The key for the `extra` map is suffix.
+ // The same key can be repeated multiple times to have multiple elements in the slice under a single key.
+ // For instance:
+ // Impersonate-Extra-Foo: one
+ // Impersonate-Extra-Foo: two
+ // results in extra["Foo"] = []string{"one", "two"}
+ ImpersonateUserExtraHeaderPrefix = "Impersonate-Extra-"
+)
+
+type impersonatingRoundTripper struct {
+ impersonate ImpersonationConfig
+ delegate http.RoundTripper
+}
+
+// NewImpersonatingRoundTripper will add an Act-As header to a request unless it has already been set.
+func NewImpersonatingRoundTripper(impersonate ImpersonationConfig, delegate http.RoundTripper) http.RoundTripper {
+ return &impersonatingRoundTripper{impersonate, delegate}
+}
+
+func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ // use the user header as marker for the rest.
+ if len(req.Header.Get(ImpersonateUserHeader)) != 0 {
+ return rt.delegate.RoundTrip(req)
+ }
+ req = utilnet.CloneRequest(req)
+ req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)
+
+ for _, group := range rt.impersonate.Groups {
+ req.Header.Add(ImpersonateGroupHeader, group)
+ }
+ for k, vv := range rt.impersonate.Extra {
+ for _, v := range vv {
+ req.Header.Add(ImpersonateUserExtraHeaderPrefix+headerKeyEscape(k), v)
+ }
+ }
+
+ return rt.delegate.RoundTrip(req)
+}
+
+func (rt *impersonatingRoundTripper) CancelRequest(req *http.Request) {
+ tryCancelRequest(rt.WrappedRoundTripper(), req)
+}
+
+func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.delegate }
+
+type bearerAuthRoundTripper struct {
+ bearer string
+ source oauth2.TokenSource
+ rt http.RoundTripper
+}
+
+// NewBearerAuthRoundTripper adds the provided bearer token to a request
+// unless the authorization header has already been set.
+func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTripper {
+ return &bearerAuthRoundTripper{bearer, nil, rt}
+}
+
+// NewBearerAuthRoundTripper 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.
+// If tokenFile is non-empty and bearer is empty, the tokenFile is read
+// immediately to populate the initial bearer token.
+func NewBearerAuthWithRefreshRoundTripper(bearer string, tokenFile string, rt http.RoundTripper) (http.RoundTripper, error) {
+ if len(tokenFile) == 0 {
+ return &bearerAuthRoundTripper{bearer, nil, rt}, nil
+ }
+ source := NewCachedFileTokenSource(tokenFile)
+ if len(bearer) == 0 {
+ token, err := source.Token()
+ if err != nil {
+ return nil, err
+ }
+ bearer = token.AccessToken
+ }
+ return &bearerAuthRoundTripper{bearer, source, rt}, nil
+}
+
+func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ if len(req.Header.Get("Authorization")) != 0 {
+ return rt.rt.RoundTrip(req)
+ }
+
+ req = utilnet.CloneRequest(req)
+ token := rt.bearer
+ if rt.source != nil {
+ if refreshedToken, err := rt.source.Token(); err == nil {
+ token = refreshedToken.AccessToken
+ }
+ }
+ req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
+ return rt.rt.RoundTrip(req)
+}
+
+func (rt *bearerAuthRoundTripper) CancelRequest(req *http.Request) {
+ 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
+ RequestVerb string
+ RequestURL string
+
+ ResponseStatus string
+ ResponseHeaders http.Header
+ ResponseErr error
+
+ Duration time.Duration
+}
+
+// newRequestInfo creates a new RequestInfo based on an http request
+func newRequestInfo(req *http.Request) *requestInfo {
+ return &requestInfo{
+ RequestURL: req.URL.String(),
+ RequestVerb: req.Method,
+ RequestHeaders: req.Header,
+ }
+}
+
+// complete adds information about the response to the requestInfo
+func (r *requestInfo) complete(response *http.Response, err error) {
+ if err != nil {
+ r.ResponseErr = err
+ return
+ }
+ r.ResponseStatus = response.Status
+ r.ResponseHeaders = response.Header
+}
+
+// toCurl returns a string that can be run as a command in a terminal (minus the body)
+func (r *requestInfo) toCurl() string {
+ headers := ""
+ for key, values := range r.RequestHeaders {
+ for _, value := range values {
+ headers += fmt.Sprintf(` -H %q`, fmt.Sprintf("%s: %s", key, value))
+ }
+ }
+
+ return fmt.Sprintf("curl -k -v -X%s %s '%s'", r.RequestVerb, headers, r.RequestURL)
+}
+
+// debuggingRoundTripper will display information about the requests passing
+// through it based on what is configured
+type debuggingRoundTripper struct {
+ delegatedRoundTripper http.RoundTripper
+
+ levels map[debugLevel]bool
+}
+
+type debugLevel int
+
+const (
+ debugJustURL debugLevel = iota
+ debugURLTiming
+ debugCurlCommand
+ debugRequestHeaders
+ debugResponseStatus
+ debugResponseHeaders
+)
+
+func newDebuggingRoundTripper(rt http.RoundTripper, levels ...debugLevel) *debuggingRoundTripper {
+ drt := &debuggingRoundTripper{
+ delegatedRoundTripper: rt,
+ levels: make(map[debugLevel]bool, len(levels)),
+ }
+ for _, v := range levels {
+ drt.levels[v] = true
+ }
+ return drt
+}
+
+func (rt *debuggingRoundTripper) CancelRequest(req *http.Request) {
+ 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] {
+ klog.Infof("%s %s", reqInfo.RequestVerb, reqInfo.RequestURL)
+ }
+ if rt.levels[debugCurlCommand] {
+ klog.Infof("%s", reqInfo.toCurl())
+
+ }
+ if rt.levels[debugRequestHeaders] {
+ klog.Infof("Request Headers:")
+ for key, values := range reqInfo.RequestHeaders {
+ for _, value := range values {
+ value = maskValue(key, value)
+ klog.Infof(" %s: %s", key, value)
+ }
+ }
+ }
+
+ startTime := time.Now()
+ response, err := rt.delegatedRoundTripper.RoundTrip(req)
+ reqInfo.Duration = time.Since(startTime)
+
+ reqInfo.complete(response, err)
+
+ 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] {
+ klog.Infof("Response Status: %s in %d milliseconds", reqInfo.ResponseStatus, reqInfo.Duration.Nanoseconds()/int64(time.Millisecond))
+ }
+ if rt.levels[debugResponseHeaders] {
+ klog.Infof("Response Headers:")
+ for key, values := range reqInfo.ResponseHeaders {
+ for _, value := range values {
+ klog.Infof(" %s: %s", key, value)
+ }
+ }
+ }
+
+ return response, err
+}
+
+func (rt *debuggingRoundTripper) WrappedRoundTripper() http.RoundTripper {
+ return rt.delegatedRoundTripper
+}
+
+func legalHeaderByte(b byte) bool {
+ return int(b) < len(legalHeaderKeyBytes) && legalHeaderKeyBytes[b]
+}
+
+func shouldEscape(b byte) bool {
+ // url.PathUnescape() returns an error if any '%' is not followed by two
+ // hexadecimal digits, so we'll intentionally encode it.
+ return !legalHeaderByte(b) || b == '%'
+}
+
+func headerKeyEscape(key string) string {
+ buf := strings.Builder{}
+ for i := 0; i < len(key); i++ {
+ b := key[i]
+ if shouldEscape(b) {
+ // %-encode bytes that should be escaped:
+ // https://tools.ietf.org/html/rfc3986#section-2.1
+ fmt.Fprintf(&buf, "%%%02X", b)
+ continue
+ }
+ buf.WriteByte(b)
+ }
+ return buf.String()
+}
+
+// legalHeaderKeyBytes was copied from net/http/lex.go's isTokenTable.
+// See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
+var legalHeaderKeyBytes = [127]bool{
+ '%': true,
+ '!': true,
+ '#': true,
+ '$': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
+}
diff --git a/vendor/k8s.io/client-go/transport/token_source.go b/vendor/k8s.io/client-go/transport/token_source.go
new file mode 100644
index 0000000..f730c39
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/token_source.go
@@ -0,0 +1,158 @@
+/*
+Copyright 2018 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 (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/oauth2"
+
+ "k8s.io/klog/v2"
+)
+
+// TokenSourceWrapTransport returns a WrapTransport that injects bearer tokens
+// authentication from an oauth2.TokenSource.
+func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) http.RoundTripper {
+ return func(rt http.RoundTripper) http.RoundTripper {
+ return &tokenSourceTransport{
+ base: rt,
+ ort: &oauth2.Transport{
+ Source: ts,
+ Base: rt,
+ },
+ }
+ }
+}
+
+// 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 {
+ return &cachingTokenSource{
+ now: time.Now,
+ leeway: 10 * time.Second,
+ base: &fileTokenSource{
+ path: path,
+ // This period was picked because it is half of the duration between when the kubelet
+ // refreshes a projected service account token and when the original token expires.
+ // Default token lifetime is 10 minutes, and the kubelet starts refreshing at 80% of lifetime.
+ // This should induce re-reading at a frequency that works with the token volume source.
+ period: time.Minute,
+ },
+ }
+}
+
+// NewCachedTokenSource returns a oauth2.TokenSource reads a token from a
+// designed TokenSource. The ts would provide the source of token.
+func NewCachedTokenSource(ts oauth2.TokenSource) oauth2.TokenSource {
+ return &cachingTokenSource{
+ now: time.Now,
+ base: ts,
+ }
+}
+
+type tokenSourceTransport struct {
+ base http.RoundTripper
+ ort http.RoundTripper
+}
+
+func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ // This is to allow --token to override other bearer token providers.
+ if req.Header.Get("Authorization") != "" {
+ return tst.base.RoundTrip(req)
+ }
+ 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
+}
+
+var _ = oauth2.TokenSource(&fileTokenSource{})
+
+func (ts *fileTokenSource) Token() (*oauth2.Token, error) {
+ tokb, err := ioutil.ReadFile(ts.path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read token file %q: %v", ts.path, err)
+ }
+ tok := strings.TrimSpace(string(tokb))
+ if len(tok) == 0 {
+ return nil, fmt.Errorf("read empty token from file %q", ts.path)
+ }
+
+ return &oauth2.Token{
+ AccessToken: tok,
+ Expiry: time.Now().Add(ts.period),
+ }, nil
+}
+
+type cachingTokenSource struct {
+ base oauth2.TokenSource
+ leeway time.Duration
+
+ sync.RWMutex
+ tok *oauth2.Token
+
+ // for testing
+ now func() time.Time
+}
+
+var _ = oauth2.TokenSource(&cachingTokenSource{})
+
+func (ts *cachingTokenSource) Token() (*oauth2.Token, error) {
+ now := ts.now()
+ // fast path
+ ts.RLock()
+ tok := ts.tok
+ ts.RUnlock()
+
+ if tok != nil && tok.Expiry.Add(-1*ts.leeway).After(now) {
+ return tok, nil
+ }
+
+ // slow path
+ ts.Lock()
+ defer ts.Unlock()
+ if tok := ts.tok; tok != nil && tok.Expiry.Add(-1*ts.leeway).After(now) {
+ return tok, nil
+ }
+
+ tok, err := ts.base.Token()
+ if err != nil {
+ if ts.tok == nil {
+ return nil, err
+ }
+ klog.Errorf("Unable to rotate token: %v", err)
+ return ts.tok, nil
+ }
+
+ ts.tok = tok
+ return tok, nil
+}
diff --git a/vendor/k8s.io/client-go/transport/transport.go b/vendor/k8s.io/client-go/transport/transport.go
new file mode 100644
index 0000000..88d8949
--- /dev/null
+++ b/vendor/k8s.io/client-go/transport/transport.go
@@ -0,0 +1,303 @@
+/*
+Copyright 2015 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 (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "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
+// or transport level security defined by the provided Config.
+func New(config *Config) (http.RoundTripper, error) {
+ // Set transport level security
+ if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.HasCertCallback() || config.TLS.Insecure) {
+ return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
+ }
+
+ var (
+ rt http.RoundTripper
+ err error
+ )
+
+ if config.Transport != nil {
+ rt = config.Transport
+ } else {
+ rt, err = tlsCache.get(config)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return HTTPWrappersForConfig(config, rt)
+}
+
+// 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 || len(c.TLS.NextProtos) > 0) {
+ return nil, nil
+ }
+ if c.HasCA() && c.TLS.Insecure {
+ return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
+ }
+ if err := loadTLSFiles(c); err != nil {
+ return nil, err
+ }
+
+ tlsConfig := &tls.Config{
+ // Can't use SSLv3 because of POODLE and BEAST
+ // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
+ // Can't use TLSv1.1 because of RC4 cipher usage
+ MinVersion: tls.VersionTLS12,
+ InsecureSkipVerify: c.TLS.Insecure,
+ ServerName: c.TLS.ServerName,
+ NextProtos: c.TLS.NextProtos,
+ }
+
+ if c.HasCA() {
+ tlsConfig.RootCAs = rootCertPool(c.TLS.CAData)
+ }
+
+ var staticCert *tls.Certificate
+ // 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)
+ if err != nil {
+ return nil, err
+ }
+ 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
+ // callback.
+ 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 {
+ return nil, err
+ }
+ // GetCert may return empty value, meaning no cert.
+ if cert != nil {
+ return cert, nil
+ }
+ }
+
+ // Both c.TLS.CertData/KeyData were unset and GetCert didn't return
+ // anything. Return an empty tls.Certificate, no client cert will
+ // be sent to the server.
+ return &tls.Certificate{}, nil
+ }
+ }
+
+ return tlsConfig, nil
+}
+
+// loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
+// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
+// either populated or were empty to start.
+func loadTLSFiles(c *Config) error {
+ var err error
+ c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
+ if err != nil {
+ 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
+ }
+
+ c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
+// or an error if an error occurred reading the file
+func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
+ if len(data) > 0 {
+ return data, nil
+ }
+ if len(file) > 0 {
+ fileData, err := ioutil.ReadFile(file)
+ if err != nil {
+ return []byte{}, err
+ }
+ return fileData, nil
+ }
+ return nil, nil
+}
+
+// rootCertPool returns nil if caData is empty. When passed along, this will mean "use system CAs".
+// When caData is not empty, it will be the ONLY information used in the CertPool.
+func rootCertPool(caData []byte) *x509.CertPool {
+ // What we really want is a copy of x509.systemRootsPool, but that isn't exposed. It's difficult to build (see the go
+ // code for a look at the platform specific insanity), so we'll use the fact that RootCAs == nil gives us the system values
+ // It doesn't allow trusting either/or, but hopefully that won't be an issue
+ if len(caData) == 0 {
+ return nil
+ }
+
+ // if we have caData, use it
+ certPool := x509.NewCertPool()
+ certPool.AppendCertsFromPEM(caData)
+ return certPool
+}
+
+// WrapperFunc wraps an http.RoundTripper when a new transport
+// is created for a client, allowing per connection behavior
+// to be injected.
+type WrapperFunc func(rt http.RoundTripper) http.RoundTripper
+
+// Wrappers accepts any number of wrappers and returns a wrapper
+// function that is the equivalent of calling each of them in order. Nil
+// values are ignored, which makes this function convenient for incrementally
+// wrapping a function.
+func Wrappers(fns ...WrapperFunc) WrapperFunc {
+ if len(fns) == 0 {
+ return nil
+ }
+ // optimize the common case of wrapping a possibly nil transport wrapper
+ // with an additional wrapper
+ if len(fns) == 2 && fns[0] == nil {
+ return fns[1]
+ }
+ return func(rt http.RoundTripper) http.RoundTripper {
+ base := rt
+ for _, fn := range fns {
+ if fn != nil {
+ base = fn(base)
+ }
+ }
+ return base
+ }
+}
+
+// ContextCanceller prevents new requests after the provided context is finished.
+// err is returned when the context is closed, allowing the caller to provide a context
+// appropriate error.
+func ContextCanceller(ctx context.Context, err error) WrapperFunc {
+ return func(rt http.RoundTripper) http.RoundTripper {
+ return &contextCanceller{
+ ctx: ctx,
+ rt: rt,
+ err: err,
+ }
+ }
+}
+
+type contextCanceller struct {
+ ctx context.Context
+ rt http.RoundTripper
+ err error
+}
+
+func (b *contextCanceller) RoundTrip(req *http.Request) (*http.Response, error) {
+ select {
+ case <-b.ctx.Done():
+ return nil, b.err
+ default:
+ 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
+ }
+}