...

Source file src/k8s.io/client-go/rest/config.go

Documentation: k8s.io/client-go/rest

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rest
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"net"
    24  	"net/http"
    25  	"net/url"
    26  	"os"
    27  	"path/filepath"
    28  	gruntime "runtime"
    29  	"strings"
    30  	"time"
    31  
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"k8s.io/client-go/pkg/version"
    36  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    37  	"k8s.io/client-go/transport"
    38  	certutil "k8s.io/client-go/util/cert"
    39  	"k8s.io/client-go/util/flowcontrol"
    40  	"k8s.io/klog/v2"
    41  )
    42  
    43  const (
    44  	DefaultQPS   float32 = 5.0
    45  	DefaultBurst int     = 10
    46  )
    47  
    48  var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
    49  
    50  // Config holds the common attributes that can be passed to a Kubernetes client on
    51  // initialization.
    52  type Config struct {
    53  	// Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
    54  	// If a URL is given then the (optional) Path of that URL represents a prefix that must
    55  	// be appended to all request URIs used to access the apiserver. This allows a frontend
    56  	// proxy to easily relocate all of the apiserver endpoints.
    57  	Host string
    58  	// APIPath is a sub-path that points to an API root.
    59  	APIPath string
    60  
    61  	// ContentConfig contains settings that affect how objects are transformed when
    62  	// sent to the server.
    63  	ContentConfig
    64  
    65  	// Server requires Basic authentication
    66  	Username string
    67  	Password string `datapolicy:"password"`
    68  
    69  	// Server requires Bearer authentication. This client will not attempt to use
    70  	// refresh tokens for an OAuth2 flow.
    71  	// TODO: demonstrate an OAuth2 compatible client.
    72  	BearerToken string `datapolicy:"token"`
    73  
    74  	// Path to a file containing a BearerToken.
    75  	// If set, the contents are periodically read.
    76  	// The last successfully read value takes precedence over BearerToken.
    77  	BearerTokenFile string
    78  
    79  	// Impersonate is the configuration that RESTClient will use for impersonation.
    80  	Impersonate ImpersonationConfig
    81  
    82  	// Server requires plugin-specified authentication.
    83  	AuthProvider *clientcmdapi.AuthProviderConfig
    84  
    85  	// Callback to persist config for AuthProvider.
    86  	AuthConfigPersister AuthProviderConfigPersister
    87  
    88  	// Exec-based authentication provider.
    89  	ExecProvider *clientcmdapi.ExecConfig
    90  
    91  	// TLSClientConfig contains settings to enable transport layer security
    92  	TLSClientConfig
    93  
    94  	// UserAgent is an optional field that specifies the caller of this request.
    95  	UserAgent string
    96  
    97  	// DisableCompression bypasses automatic GZip compression requests to the
    98  	// server.
    99  	DisableCompression bool
   100  
   101  	// Transport may be used for custom HTTP behavior. This attribute may not
   102  	// be specified with the TLS client certificate options. Use WrapTransport
   103  	// to provide additional per-server middleware behavior.
   104  	Transport http.RoundTripper
   105  	// WrapTransport will be invoked for custom HTTP behavior after the underlying
   106  	// transport is initialized (either the transport created from TLSClientConfig,
   107  	// Transport, or http.DefaultTransport). The config may layer other RoundTrippers
   108  	// on top of the returned RoundTripper.
   109  	//
   110  	// A future release will change this field to an array. Use config.Wrap()
   111  	// instead of setting this value directly.
   112  	WrapTransport transport.WrapperFunc
   113  
   114  	// QPS indicates the maximum QPS to the master from this client.
   115  	// If it's zero, the created RESTClient will use DefaultQPS: 5
   116  	QPS float32
   117  
   118  	// Maximum burst for throttle.
   119  	// If it's zero, the created RESTClient will use DefaultBurst: 10.
   120  	Burst int
   121  
   122  	// Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
   123  	RateLimiter flowcontrol.RateLimiter
   124  
   125  	// WarningHandler handles warnings in server responses.
   126  	// If not set, the default warning handler is used.
   127  	// See documentation for SetDefaultWarningHandler() for details.
   128  	WarningHandler WarningHandler
   129  
   130  	// The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
   131  	Timeout time.Duration
   132  
   133  	// Dial specifies the dial function for creating unencrypted TCP connections.
   134  	Dial func(ctx context.Context, network, address string) (net.Conn, error)
   135  
   136  	// Proxy is the proxy func to be used for all requests made by this
   137  	// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
   138  	// returns a nil *URL, no proxy is used.
   139  	//
   140  	// socks5 proxying does not currently support spdy streaming endpoints.
   141  	Proxy func(*http.Request) (*url.URL, error)
   142  
   143  	// Version forces a specific version to be used (if registered)
   144  	// Do we need this?
   145  	// Version string
   146  }
   147  
   148  var _ fmt.Stringer = new(Config)
   149  var _ fmt.GoStringer = new(Config)
   150  
   151  type sanitizedConfig *Config
   152  
   153  type sanitizedAuthConfigPersister struct{ AuthProviderConfigPersister }
   154  
   155  func (sanitizedAuthConfigPersister) GoString() string {
   156  	return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
   157  }
   158  func (sanitizedAuthConfigPersister) String() string {
   159  	return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
   160  }
   161  
   162  type sanitizedObject struct{ runtime.Object }
   163  
   164  func (sanitizedObject) GoString() string {
   165  	return "runtime.Object(--- REDACTED ---)"
   166  }
   167  func (sanitizedObject) String() string {
   168  	return "runtime.Object(--- REDACTED ---)"
   169  }
   170  
   171  // GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
   172  // to prevent accidental leaking via logs.
   173  func (c *Config) GoString() string {
   174  	return c.String()
   175  }
   176  
   177  // String implements fmt.Stringer and sanitizes sensitive fields of Config to
   178  // prevent accidental leaking via logs.
   179  func (c *Config) String() string {
   180  	if c == nil {
   181  		return "<nil>"
   182  	}
   183  	cc := sanitizedConfig(CopyConfig(c))
   184  	// Explicitly mark non-empty credential fields as redacted.
   185  	if cc.Password != "" {
   186  		cc.Password = "--- REDACTED ---"
   187  	}
   188  	if cc.BearerToken != "" {
   189  		cc.BearerToken = "--- REDACTED ---"
   190  	}
   191  	if cc.AuthConfigPersister != nil {
   192  		cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
   193  	}
   194  	if cc.ExecProvider != nil && cc.ExecProvider.Config != nil {
   195  		cc.ExecProvider.Config = sanitizedObject{Object: cc.ExecProvider.Config}
   196  	}
   197  	return fmt.Sprintf("%#v", cc)
   198  }
   199  
   200  // ImpersonationConfig has all the available impersonation options
   201  type ImpersonationConfig struct {
   202  	// UserName is the username to impersonate on each request.
   203  	UserName string
   204  	// UID is a unique value that identifies the user.
   205  	UID string
   206  	// Groups are the groups to impersonate on each request.
   207  	Groups []string
   208  	// Extra is a free-form field which can be used to link some authentication information
   209  	// to authorization information.  This field allows you to impersonate it.
   210  	Extra map[string][]string
   211  }
   212  
   213  // +k8s:deepcopy-gen=true
   214  // TLSClientConfig contains settings to enable transport layer security
   215  type TLSClientConfig struct {
   216  	// Server should be accessed without verifying the TLS certificate. For testing only.
   217  	Insecure bool
   218  	// ServerName is passed to the server for SNI and is used in the client to check server
   219  	// certificates against. If ServerName is empty, the hostname used to contact the
   220  	// server is used.
   221  	ServerName string
   222  
   223  	// Server requires TLS client certificate authentication
   224  	CertFile string
   225  	// Server requires TLS client certificate authentication
   226  	KeyFile string
   227  	// Trusted root certificates for server
   228  	CAFile string
   229  
   230  	// CertData holds PEM-encoded bytes (typically read from a client certificate file).
   231  	// CertData takes precedence over CertFile
   232  	CertData []byte
   233  	// KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
   234  	// KeyData takes precedence over KeyFile
   235  	KeyData []byte `datapolicy:"security-key"`
   236  	// CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
   237  	// CAData takes precedence over CAFile
   238  	CAData []byte
   239  
   240  	// NextProtos is a list of supported application level protocols, in order of preference.
   241  	// Used to populate tls.Config.NextProtos.
   242  	// 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).
   243  	// To use only http/1.1, set to ["http/1.1"].
   244  	NextProtos []string
   245  }
   246  
   247  var _ fmt.Stringer = TLSClientConfig{}
   248  var _ fmt.GoStringer = TLSClientConfig{}
   249  
   250  type sanitizedTLSClientConfig TLSClientConfig
   251  
   252  // GoString implements fmt.GoStringer and sanitizes sensitive fields of
   253  // TLSClientConfig to prevent accidental leaking via logs.
   254  func (c TLSClientConfig) GoString() string {
   255  	return c.String()
   256  }
   257  
   258  // String implements fmt.Stringer and sanitizes sensitive fields of
   259  // TLSClientConfig to prevent accidental leaking via logs.
   260  func (c TLSClientConfig) String() string {
   261  	cc := sanitizedTLSClientConfig{
   262  		Insecure:   c.Insecure,
   263  		ServerName: c.ServerName,
   264  		CertFile:   c.CertFile,
   265  		KeyFile:    c.KeyFile,
   266  		CAFile:     c.CAFile,
   267  		CertData:   c.CertData,
   268  		KeyData:    c.KeyData,
   269  		CAData:     c.CAData,
   270  		NextProtos: c.NextProtos,
   271  	}
   272  	// Explicitly mark non-empty credential fields as redacted.
   273  	if len(cc.CertData) != 0 {
   274  		cc.CertData = []byte("--- TRUNCATED ---")
   275  	}
   276  	if len(cc.KeyData) != 0 {
   277  		cc.KeyData = []byte("--- REDACTED ---")
   278  	}
   279  	return fmt.Sprintf("%#v", cc)
   280  }
   281  
   282  type ContentConfig struct {
   283  	// AcceptContentTypes specifies the types the client will accept and is optional.
   284  	// If not set, ContentType will be used to define the Accept header
   285  	AcceptContentTypes string
   286  	// ContentType specifies the wire format used to communicate with the server.
   287  	// This value will be set as the Accept header on requests made to the server, and
   288  	// as the default content type on any object sent to the server. If not set,
   289  	// "application/json" is used.
   290  	ContentType string
   291  	// GroupVersion is the API version to talk to. Must be provided when initializing
   292  	// a RESTClient directly. When initializing a Client, will be set with the default
   293  	// code version.
   294  	GroupVersion *schema.GroupVersion
   295  	// NegotiatedSerializer is used for obtaining encoders and decoders for multiple
   296  	// supported media types.
   297  	//
   298  	// TODO: NegotiatedSerializer will be phased out as internal clients are removed
   299  	//   from Kubernetes.
   300  	NegotiatedSerializer runtime.NegotiatedSerializer
   301  }
   302  
   303  // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
   304  // object. Note that a RESTClient may require fields that are optional when initializing a Client.
   305  // A RESTClient created by this method is generic - it expects to operate on an API that follows
   306  // the Kubernetes conventions, but may not be the Kubernetes API.
   307  // RESTClientFor is equivalent to calling RESTClientForConfigAndClient(config, httpClient),
   308  // where httpClient was generated with HTTPClientFor(config).
   309  func RESTClientFor(config *Config) (*RESTClient, error) {
   310  	if config.GroupVersion == nil {
   311  		return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
   312  	}
   313  	if config.NegotiatedSerializer == nil {
   314  		return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
   315  	}
   316  
   317  	// Validate config.Host before constructing the transport/client so we can fail fast.
   318  	// ServerURL will be obtained later in RESTClientForConfigAndClient()
   319  	_, _, err := DefaultServerUrlFor(config)
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  
   324  	httpClient, err := HTTPClientFor(config)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	return RESTClientForConfigAndClient(config, httpClient)
   330  }
   331  
   332  // RESTClientForConfigAndClient returns a RESTClient that satisfies the requested attributes on a
   333  // client Config object.
   334  // Unlike RESTClientFor, RESTClientForConfigAndClient allows to pass an http.Client that is shared
   335  // between all the API Groups and Versions.
   336  // Note that the http client takes precedence over the transport values configured.
   337  // The http client defaults to the `http.DefaultClient` if nil.
   338  func RESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
   339  	if config.GroupVersion == nil {
   340  		return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
   341  	}
   342  	if config.NegotiatedSerializer == nil {
   343  		return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
   344  	}
   345  
   346  	baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	rateLimiter := config.RateLimiter
   352  	if rateLimiter == nil {
   353  		qps := config.QPS
   354  		if config.QPS == 0.0 {
   355  			qps = DefaultQPS
   356  		}
   357  		burst := config.Burst
   358  		if config.Burst == 0 {
   359  			burst = DefaultBurst
   360  		}
   361  		if qps > 0 {
   362  			rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
   363  		}
   364  	}
   365  
   366  	var gv schema.GroupVersion
   367  	if config.GroupVersion != nil {
   368  		gv = *config.GroupVersion
   369  	}
   370  	clientContent := ClientContentConfig{
   371  		AcceptContentTypes: config.AcceptContentTypes,
   372  		ContentType:        config.ContentType,
   373  		GroupVersion:       gv,
   374  		Negotiator:         runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
   375  	}
   376  
   377  	restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
   378  	if err == nil && config.WarningHandler != nil {
   379  		restClient.warningHandler = config.WarningHandler
   380  	}
   381  	return restClient, err
   382  }
   383  
   384  // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
   385  // the config.Version to be empty.
   386  func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
   387  	if config.NegotiatedSerializer == nil {
   388  		return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
   389  	}
   390  
   391  	// Validate config.Host before constructing the transport/client so we can fail fast.
   392  	// ServerURL will be obtained later in UnversionedRESTClientForConfigAndClient()
   393  	_, _, err := DefaultServerUrlFor(config)
   394  	if err != nil {
   395  		return nil, err
   396  	}
   397  
   398  	httpClient, err := HTTPClientFor(config)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  
   403  	return UnversionedRESTClientForConfigAndClient(config, httpClient)
   404  }
   405  
   406  // UnversionedRESTClientForConfigAndClient is the same as RESTClientForConfigAndClient,
   407  // except that it allows the config.Version to be empty.
   408  func UnversionedRESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
   409  	if config.NegotiatedSerializer == nil {
   410  		return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
   411  	}
   412  
   413  	baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	rateLimiter := config.RateLimiter
   419  	if rateLimiter == nil {
   420  		qps := config.QPS
   421  		if config.QPS == 0.0 {
   422  			qps = DefaultQPS
   423  		}
   424  		burst := config.Burst
   425  		if config.Burst == 0 {
   426  			burst = DefaultBurst
   427  		}
   428  		if qps > 0 {
   429  			rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
   430  		}
   431  	}
   432  
   433  	gv := metav1.SchemeGroupVersion
   434  	if config.GroupVersion != nil {
   435  		gv = *config.GroupVersion
   436  	}
   437  	clientContent := ClientContentConfig{
   438  		AcceptContentTypes: config.AcceptContentTypes,
   439  		ContentType:        config.ContentType,
   440  		GroupVersion:       gv,
   441  		Negotiator:         runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
   442  	}
   443  
   444  	restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
   445  	if err == nil && config.WarningHandler != nil {
   446  		restClient.warningHandler = config.WarningHandler
   447  	}
   448  	return restClient, err
   449  }
   450  
   451  // SetKubernetesDefaults sets default values on the provided client config for accessing the
   452  // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
   453  func SetKubernetesDefaults(config *Config) error {
   454  	if len(config.UserAgent) == 0 {
   455  		config.UserAgent = DefaultKubernetesUserAgent()
   456  	}
   457  	return nil
   458  }
   459  
   460  // adjustCommit returns sufficient significant figures of the commit's git hash.
   461  func adjustCommit(c string) string {
   462  	if len(c) == 0 {
   463  		return "unknown"
   464  	}
   465  	if len(c) > 7 {
   466  		return c[:7]
   467  	}
   468  	return c
   469  }
   470  
   471  // adjustVersion strips "alpha", "beta", etc. from version in form
   472  // major.minor.patch-[alpha|beta|etc].
   473  func adjustVersion(v string) string {
   474  	if len(v) == 0 {
   475  		return "unknown"
   476  	}
   477  	seg := strings.SplitN(v, "-", 2)
   478  	return seg[0]
   479  }
   480  
   481  // adjustCommand returns the last component of the
   482  // OS-specific command path for use in User-Agent.
   483  func adjustCommand(p string) string {
   484  	// Unlikely, but better than returning "".
   485  	if len(p) == 0 {
   486  		return "unknown"
   487  	}
   488  	return filepath.Base(p)
   489  }
   490  
   491  // buildUserAgent builds a User-Agent string from given args.
   492  func buildUserAgent(command, version, os, arch, commit string) string {
   493  	return fmt.Sprintf(
   494  		"%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
   495  }
   496  
   497  // DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
   498  func DefaultKubernetesUserAgent() string {
   499  	return buildUserAgent(
   500  		adjustCommand(os.Args[0]),
   501  		adjustVersion(version.Get().GitVersion),
   502  		gruntime.GOOS,
   503  		gruntime.GOARCH,
   504  		adjustCommit(version.Get().GitCommit))
   505  }
   506  
   507  // InClusterConfig returns a config object which uses the service account
   508  // kubernetes gives to pods. It's intended for clients that expect to be
   509  // running inside a pod running on kubernetes. It will return ErrNotInCluster
   510  // if called from a process not running in a kubernetes environment.
   511  func InClusterConfig() (*Config, error) {
   512  	const (
   513  		tokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
   514  		rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
   515  	)
   516  	host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
   517  	if len(host) == 0 || len(port) == 0 {
   518  		return nil, ErrNotInCluster
   519  	}
   520  
   521  	token, err := os.ReadFile(tokenFile)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	tlsClientConfig := TLSClientConfig{}
   527  
   528  	if _, err := certutil.NewPool(rootCAFile); err != nil {
   529  		klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
   530  	} else {
   531  		tlsClientConfig.CAFile = rootCAFile
   532  	}
   533  
   534  	return &Config{
   535  		// TODO: switch to using cluster DNS.
   536  		Host:            "https://" + net.JoinHostPort(host, port),
   537  		TLSClientConfig: tlsClientConfig,
   538  		BearerToken:     string(token),
   539  		BearerTokenFile: tokenFile,
   540  	}, nil
   541  }
   542  
   543  // IsConfigTransportTLS returns true if and only if the provided
   544  // config will result in a protected connection to the server when it
   545  // is passed to restclient.RESTClientFor().  Use to determine when to
   546  // send credentials over the wire.
   547  //
   548  // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
   549  // still possible.
   550  func IsConfigTransportTLS(config Config) bool {
   551  	baseURL, _, err := DefaultServerUrlFor(&config)
   552  	if err != nil {
   553  		return false
   554  	}
   555  	return baseURL.Scheme == "https"
   556  }
   557  
   558  // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
   559  // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
   560  // either populated or were empty to start.
   561  func LoadTLSFiles(c *Config) error {
   562  	var err error
   563  	c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
   564  	if err != nil {
   565  		return err
   566  	}
   567  
   568  	c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
   569  	if err != nil {
   570  		return err
   571  	}
   572  
   573  	c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
   574  	return err
   575  }
   576  
   577  // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
   578  // or an error if an error occurred reading the file
   579  func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
   580  	if len(data) > 0 {
   581  		return data, nil
   582  	}
   583  	if len(file) > 0 {
   584  		fileData, err := os.ReadFile(file)
   585  		if err != nil {
   586  			return []byte{}, err
   587  		}
   588  		return fileData, nil
   589  	}
   590  	return nil, nil
   591  }
   592  
   593  func AddUserAgent(config *Config, userAgent string) *Config {
   594  	fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
   595  	config.UserAgent = fullUserAgent
   596  	return config
   597  }
   598  
   599  // AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) and custom transports (WrapTransport, Transport) removed
   600  func AnonymousClientConfig(config *Config) *Config {
   601  	// copy only known safe fields
   602  	return &Config{
   603  		Host:          config.Host,
   604  		APIPath:       config.APIPath,
   605  		ContentConfig: config.ContentConfig,
   606  		TLSClientConfig: TLSClientConfig{
   607  			Insecure:   config.Insecure,
   608  			ServerName: config.ServerName,
   609  			CAFile:     config.TLSClientConfig.CAFile,
   610  			CAData:     config.TLSClientConfig.CAData,
   611  			NextProtos: config.TLSClientConfig.NextProtos,
   612  		},
   613  		RateLimiter:        config.RateLimiter,
   614  		WarningHandler:     config.WarningHandler,
   615  		UserAgent:          config.UserAgent,
   616  		DisableCompression: config.DisableCompression,
   617  		QPS:                config.QPS,
   618  		Burst:              config.Burst,
   619  		Timeout:            config.Timeout,
   620  		Dial:               config.Dial,
   621  		Proxy:              config.Proxy,
   622  	}
   623  }
   624  
   625  // CopyConfig returns a copy of the given config
   626  func CopyConfig(config *Config) *Config {
   627  	c := &Config{
   628  		Host:            config.Host,
   629  		APIPath:         config.APIPath,
   630  		ContentConfig:   config.ContentConfig,
   631  		Username:        config.Username,
   632  		Password:        config.Password,
   633  		BearerToken:     config.BearerToken,
   634  		BearerTokenFile: config.BearerTokenFile,
   635  		Impersonate: ImpersonationConfig{
   636  			UserName: config.Impersonate.UserName,
   637  			UID:      config.Impersonate.UID,
   638  			Groups:   config.Impersonate.Groups,
   639  			Extra:    config.Impersonate.Extra,
   640  		},
   641  		AuthProvider:        config.AuthProvider,
   642  		AuthConfigPersister: config.AuthConfigPersister,
   643  		ExecProvider:        config.ExecProvider,
   644  		TLSClientConfig: TLSClientConfig{
   645  			Insecure:   config.TLSClientConfig.Insecure,
   646  			ServerName: config.TLSClientConfig.ServerName,
   647  			CertFile:   config.TLSClientConfig.CertFile,
   648  			KeyFile:    config.TLSClientConfig.KeyFile,
   649  			CAFile:     config.TLSClientConfig.CAFile,
   650  			CertData:   config.TLSClientConfig.CertData,
   651  			KeyData:    config.TLSClientConfig.KeyData,
   652  			CAData:     config.TLSClientConfig.CAData,
   653  			NextProtos: config.TLSClientConfig.NextProtos,
   654  		},
   655  		UserAgent:          config.UserAgent,
   656  		DisableCompression: config.DisableCompression,
   657  		Transport:          config.Transport,
   658  		WrapTransport:      config.WrapTransport,
   659  		QPS:                config.QPS,
   660  		Burst:              config.Burst,
   661  		RateLimiter:        config.RateLimiter,
   662  		WarningHandler:     config.WarningHandler,
   663  		Timeout:            config.Timeout,
   664  		Dial:               config.Dial,
   665  		Proxy:              config.Proxy,
   666  	}
   667  	if config.ExecProvider != nil && config.ExecProvider.Config != nil {
   668  		c.ExecProvider.Config = config.ExecProvider.Config.DeepCopyObject()
   669  	}
   670  	return c
   671  }
   672  

View as plain text