k8s.io/client-go@v0.31.1/rest/request.go (about)

     1  /*
     2  Copyright 2014 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  	"bytes"
    21  	"context"
    22  	"encoding/hex"
    23  	"fmt"
    24  	"io"
    25  	"mime"
    26  	"net/http"
    27  	"net/http/httptrace"
    28  	"net/url"
    29  	"os"
    30  	"path"
    31  	"reflect"
    32  	"strconv"
    33  	"strings"
    34  	"sync"
    35  	"time"
    36  
    37  	"golang.org/x/net/http2"
    38  
    39  	"k8s.io/apimachinery/pkg/api/errors"
    40  	"k8s.io/apimachinery/pkg/api/meta"
    41  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    42  	"k8s.io/apimachinery/pkg/conversion"
    43  	"k8s.io/apimachinery/pkg/runtime"
    44  	"k8s.io/apimachinery/pkg/runtime/schema"
    45  	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
    46  	"k8s.io/apimachinery/pkg/util/net"
    47  	"k8s.io/apimachinery/pkg/watch"
    48  	clientfeatures "k8s.io/client-go/features"
    49  	restclientwatch "k8s.io/client-go/rest/watch"
    50  	"k8s.io/client-go/tools/metrics"
    51  	"k8s.io/client-go/util/flowcontrol"
    52  	"k8s.io/klog/v2"
    53  	"k8s.io/utils/clock"
    54  )
    55  
    56  var (
    57  	// longThrottleLatency defines threshold for logging requests. All requests being
    58  	// throttled (via the provided rateLimiter) for more than longThrottleLatency will
    59  	// be logged.
    60  	longThrottleLatency = 50 * time.Millisecond
    61  
    62  	// extraLongThrottleLatency defines the threshold for logging requests at log level 2.
    63  	extraLongThrottleLatency = 1 * time.Second
    64  )
    65  
    66  // HTTPClient is an interface for testing a request object.
    67  type HTTPClient interface {
    68  	Do(req *http.Request) (*http.Response, error)
    69  }
    70  
    71  // ResponseWrapper is an interface for getting a response.
    72  // The response may be either accessed as a raw data (the whole output is put into memory) or as a stream.
    73  type ResponseWrapper interface {
    74  	DoRaw(context.Context) ([]byte, error)
    75  	Stream(context.Context) (io.ReadCloser, error)
    76  }
    77  
    78  // RequestConstructionError is returned when there's an error assembling a request.
    79  type RequestConstructionError struct {
    80  	Err error
    81  }
    82  
    83  // Error returns a textual description of 'r'.
    84  func (r *RequestConstructionError) Error() string {
    85  	return fmt.Sprintf("request construction error: '%v'", r.Err)
    86  }
    87  
    88  var noBackoff = &NoBackoff{}
    89  
    90  type requestRetryFunc func(maxRetries int) WithRetry
    91  
    92  func defaultRequestRetryFn(maxRetries int) WithRetry {
    93  	return &withRetry{maxRetries: maxRetries}
    94  }
    95  
    96  // Request allows for building up a request to a server in a chained fashion.
    97  // Any errors are stored until the end of your call, so you only have to
    98  // check once.
    99  type Request struct {
   100  	c *RESTClient
   101  
   102  	warningHandler WarningHandler
   103  
   104  	rateLimiter flowcontrol.RateLimiter
   105  	backoff     BackoffManager
   106  	timeout     time.Duration
   107  	maxRetries  int
   108  
   109  	// generic components accessible via method setters
   110  	verb       string
   111  	pathPrefix string
   112  	subpath    string
   113  	params     url.Values
   114  	headers    http.Header
   115  
   116  	// structural elements of the request that are part of the Kubernetes API conventions
   117  	namespace    string
   118  	namespaceSet bool
   119  	resource     string
   120  	resourceName string
   121  	subresource  string
   122  
   123  	// output
   124  	err error
   125  
   126  	// only one of body / bodyBytes may be set. requests using body are not retriable.
   127  	body      io.Reader
   128  	bodyBytes []byte
   129  
   130  	retryFn requestRetryFunc
   131  }
   132  
   133  // NewRequest creates a new request helper object for accessing runtime.Objects on a server.
   134  func NewRequest(c *RESTClient) *Request {
   135  	var backoff BackoffManager
   136  	if c.createBackoffMgr != nil {
   137  		backoff = c.createBackoffMgr()
   138  	}
   139  	if backoff == nil {
   140  		backoff = noBackoff
   141  	}
   142  
   143  	var pathPrefix string
   144  	if c.base != nil {
   145  		pathPrefix = path.Join("/", c.base.Path, c.versionedAPIPath)
   146  	} else {
   147  		pathPrefix = path.Join("/", c.versionedAPIPath)
   148  	}
   149  
   150  	var timeout time.Duration
   151  	if c.Client != nil {
   152  		timeout = c.Client.Timeout
   153  	}
   154  
   155  	r := &Request{
   156  		c:              c,
   157  		rateLimiter:    c.rateLimiter,
   158  		backoff:        backoff,
   159  		timeout:        timeout,
   160  		pathPrefix:     pathPrefix,
   161  		maxRetries:     10,
   162  		retryFn:        defaultRequestRetryFn,
   163  		warningHandler: c.warningHandler,
   164  	}
   165  
   166  	switch {
   167  	case len(c.content.AcceptContentTypes) > 0:
   168  		r.SetHeader("Accept", c.content.AcceptContentTypes)
   169  	case len(c.content.ContentType) > 0:
   170  		r.SetHeader("Accept", c.content.ContentType+", */*")
   171  	}
   172  	return r
   173  }
   174  
   175  // NewRequestWithClient creates a Request with an embedded RESTClient for use in test scenarios.
   176  func NewRequestWithClient(base *url.URL, versionedAPIPath string, content ClientContentConfig, client *http.Client) *Request {
   177  	return NewRequest(&RESTClient{
   178  		base:             base,
   179  		versionedAPIPath: versionedAPIPath,
   180  		content:          content,
   181  		Client:           client,
   182  	})
   183  }
   184  
   185  // Verb sets the verb this request will use.
   186  func (r *Request) Verb(verb string) *Request {
   187  	r.verb = verb
   188  	return r
   189  }
   190  
   191  // Prefix adds segments to the relative beginning to the request path. These
   192  // items will be placed before the optional Namespace, Resource, or Name sections.
   193  // Setting AbsPath will clear any previously set Prefix segments
   194  func (r *Request) Prefix(segments ...string) *Request {
   195  	if r.err != nil {
   196  		return r
   197  	}
   198  	r.pathPrefix = path.Join(r.pathPrefix, path.Join(segments...))
   199  	return r
   200  }
   201  
   202  // Suffix appends segments to the end of the path. These items will be placed after the prefix and optional
   203  // Namespace, Resource, or Name sections.
   204  func (r *Request) Suffix(segments ...string) *Request {
   205  	if r.err != nil {
   206  		return r
   207  	}
   208  	r.subpath = path.Join(r.subpath, path.Join(segments...))
   209  	return r
   210  }
   211  
   212  // Resource sets the resource to access (<resource>/[ns/<namespace>/]<name>)
   213  func (r *Request) Resource(resource string) *Request {
   214  	if r.err != nil {
   215  		return r
   216  	}
   217  	if len(r.resource) != 0 {
   218  		r.err = fmt.Errorf("resource already set to %q, cannot change to %q", r.resource, resource)
   219  		return r
   220  	}
   221  	if msgs := IsValidPathSegmentName(resource); len(msgs) != 0 {
   222  		r.err = fmt.Errorf("invalid resource %q: %v", resource, msgs)
   223  		return r
   224  	}
   225  	r.resource = resource
   226  	return r
   227  }
   228  
   229  // BackOff sets the request's backoff manager to the one specified,
   230  // or defaults to the stub implementation if nil is provided
   231  func (r *Request) BackOff(manager BackoffManager) *Request {
   232  	if manager == nil {
   233  		r.backoff = &NoBackoff{}
   234  		return r
   235  	}
   236  
   237  	r.backoff = manager
   238  	return r
   239  }
   240  
   241  // WarningHandler sets the handler this client uses when warning headers are encountered.
   242  // If set to nil, this client will use the default warning handler (see SetDefaultWarningHandler).
   243  func (r *Request) WarningHandler(handler WarningHandler) *Request {
   244  	r.warningHandler = handler
   245  	return r
   246  }
   247  
   248  // Throttle receives a rate-limiter and sets or replaces an existing request limiter
   249  func (r *Request) Throttle(limiter flowcontrol.RateLimiter) *Request {
   250  	r.rateLimiter = limiter
   251  	return r
   252  }
   253  
   254  // SubResource sets a sub-resource path which can be multiple segments after the resource
   255  // name but before the suffix.
   256  func (r *Request) SubResource(subresources ...string) *Request {
   257  	if r.err != nil {
   258  		return r
   259  	}
   260  	subresource := path.Join(subresources...)
   261  	if len(r.subresource) != 0 {
   262  		r.err = fmt.Errorf("subresource already set to %q, cannot change to %q", r.subresource, subresource)
   263  		return r
   264  	}
   265  	for _, s := range subresources {
   266  		if msgs := IsValidPathSegmentName(s); len(msgs) != 0 {
   267  			r.err = fmt.Errorf("invalid subresource %q: %v", s, msgs)
   268  			return r
   269  		}
   270  	}
   271  	r.subresource = subresource
   272  	return r
   273  }
   274  
   275  // Name sets the name of a resource to access (<resource>/[ns/<namespace>/]<name>)
   276  func (r *Request) Name(resourceName string) *Request {
   277  	if r.err != nil {
   278  		return r
   279  	}
   280  	if len(resourceName) == 0 {
   281  		r.err = fmt.Errorf("resource name may not be empty")
   282  		return r
   283  	}
   284  	if len(r.resourceName) != 0 {
   285  		r.err = fmt.Errorf("resource name already set to %q, cannot change to %q", r.resourceName, resourceName)
   286  		return r
   287  	}
   288  	if msgs := IsValidPathSegmentName(resourceName); len(msgs) != 0 {
   289  		r.err = fmt.Errorf("invalid resource name %q: %v", resourceName, msgs)
   290  		return r
   291  	}
   292  	r.resourceName = resourceName
   293  	return r
   294  }
   295  
   296  // Namespace applies the namespace scope to a request (<resource>/[ns/<namespace>/]<name>)
   297  func (r *Request) Namespace(namespace string) *Request {
   298  	if r.err != nil {
   299  		return r
   300  	}
   301  	if r.namespaceSet {
   302  		r.err = fmt.Errorf("namespace already set to %q, cannot change to %q", r.namespace, namespace)
   303  		return r
   304  	}
   305  	if msgs := IsValidPathSegmentName(namespace); len(msgs) != 0 {
   306  		r.err = fmt.Errorf("invalid namespace %q: %v", namespace, msgs)
   307  		return r
   308  	}
   309  	r.namespaceSet = true
   310  	r.namespace = namespace
   311  	return r
   312  }
   313  
   314  // NamespaceIfScoped is a convenience function to set a namespace if scoped is true
   315  func (r *Request) NamespaceIfScoped(namespace string, scoped bool) *Request {
   316  	if scoped {
   317  		return r.Namespace(namespace)
   318  	}
   319  	return r
   320  }
   321  
   322  // AbsPath overwrites an existing path with the segments provided. Trailing slashes are preserved
   323  // when a single segment is passed.
   324  func (r *Request) AbsPath(segments ...string) *Request {
   325  	if r.err != nil {
   326  		return r
   327  	}
   328  	r.pathPrefix = path.Join(r.c.base.Path, path.Join(segments...))
   329  	if len(segments) == 1 && (len(r.c.base.Path) > 1 || len(segments[0]) > 1) && strings.HasSuffix(segments[0], "/") {
   330  		// preserve any trailing slashes for legacy behavior
   331  		r.pathPrefix += "/"
   332  	}
   333  	return r
   334  }
   335  
   336  // RequestURI overwrites existing path and parameters with the value of the provided server relative
   337  // URI.
   338  func (r *Request) RequestURI(uri string) *Request {
   339  	if r.err != nil {
   340  		return r
   341  	}
   342  	locator, err := url.Parse(uri)
   343  	if err != nil {
   344  		r.err = err
   345  		return r
   346  	}
   347  	r.pathPrefix = locator.Path
   348  	if len(locator.Query()) > 0 {
   349  		if r.params == nil {
   350  			r.params = make(url.Values)
   351  		}
   352  		for k, v := range locator.Query() {
   353  			r.params[k] = v
   354  		}
   355  	}
   356  	return r
   357  }
   358  
   359  // Param creates a query parameter with the given string value.
   360  func (r *Request) Param(paramName, s string) *Request {
   361  	if r.err != nil {
   362  		return r
   363  	}
   364  	return r.setParam(paramName, s)
   365  }
   366  
   367  // VersionedParams will take the provided object, serialize it to a map[string][]string using the
   368  // implicit RESTClient API version and the default parameter codec, and then add those as parameters
   369  // to the request. Use this to provide versioned query parameters from client libraries.
   370  // VersionedParams will not write query parameters that have omitempty set and are empty. If a
   371  // parameter has already been set it is appended to (Params and VersionedParams are additive).
   372  func (r *Request) VersionedParams(obj runtime.Object, codec runtime.ParameterCodec) *Request {
   373  	return r.SpecificallyVersionedParams(obj, codec, r.c.content.GroupVersion)
   374  }
   375  
   376  func (r *Request) SpecificallyVersionedParams(obj runtime.Object, codec runtime.ParameterCodec, version schema.GroupVersion) *Request {
   377  	if r.err != nil {
   378  		return r
   379  	}
   380  	params, err := codec.EncodeParameters(obj, version)
   381  	if err != nil {
   382  		r.err = err
   383  		return r
   384  	}
   385  	for k, v := range params {
   386  		if r.params == nil {
   387  			r.params = make(url.Values)
   388  		}
   389  		r.params[k] = append(r.params[k], v...)
   390  	}
   391  	return r
   392  }
   393  
   394  func (r *Request) setParam(paramName, value string) *Request {
   395  	if r.params == nil {
   396  		r.params = make(url.Values)
   397  	}
   398  	r.params[paramName] = append(r.params[paramName], value)
   399  	return r
   400  }
   401  
   402  func (r *Request) SetHeader(key string, values ...string) *Request {
   403  	if r.headers == nil {
   404  		r.headers = http.Header{}
   405  	}
   406  	r.headers.Del(key)
   407  	for _, value := range values {
   408  		r.headers.Add(key, value)
   409  	}
   410  	return r
   411  }
   412  
   413  // Timeout makes the request use the given duration as an overall timeout for the
   414  // request. Additionally, if set passes the value as "timeout" parameter in URL.
   415  func (r *Request) Timeout(d time.Duration) *Request {
   416  	if r.err != nil {
   417  		return r
   418  	}
   419  	r.timeout = d
   420  	return r
   421  }
   422  
   423  // MaxRetries makes the request use the given integer as a ceiling of retrying upon receiving
   424  // "Retry-After" headers and 429 status-code in the response. The default is 10 unless this
   425  // function is specifically called with a different value.
   426  // A zero maxRetries prevent it from doing retires and return an error immediately.
   427  func (r *Request) MaxRetries(maxRetries int) *Request {
   428  	if maxRetries < 0 {
   429  		maxRetries = 0
   430  	}
   431  	r.maxRetries = maxRetries
   432  	return r
   433  }
   434  
   435  // Body makes the request use obj as the body. Optional.
   436  // If obj is a string, try to read a file of that name.
   437  // If obj is a []byte, send it directly.
   438  // If obj is an io.Reader, use it directly.
   439  // If obj is a runtime.Object, marshal it correctly, and set Content-Type header.
   440  // If obj is a runtime.Object and nil, do nothing.
   441  // Otherwise, set an error.
   442  func (r *Request) Body(obj interface{}) *Request {
   443  	if r.err != nil {
   444  		return r
   445  	}
   446  	switch t := obj.(type) {
   447  	case string:
   448  		data, err := os.ReadFile(t)
   449  		if err != nil {
   450  			r.err = err
   451  			return r
   452  		}
   453  		glogBody("Request Body", data)
   454  		r.body = nil
   455  		r.bodyBytes = data
   456  	case []byte:
   457  		glogBody("Request Body", t)
   458  		r.body = nil
   459  		r.bodyBytes = t
   460  	case io.Reader:
   461  		r.body = t
   462  		r.bodyBytes = nil
   463  	case runtime.Object:
   464  		// callers may pass typed interface pointers, therefore we must check nil with reflection
   465  		if reflect.ValueOf(t).IsNil() {
   466  			return r
   467  		}
   468  		encoder, err := r.c.content.Negotiator.Encoder(r.c.content.ContentType, nil)
   469  		if err != nil {
   470  			r.err = err
   471  			return r
   472  		}
   473  		data, err := runtime.Encode(encoder, t)
   474  		if err != nil {
   475  			r.err = err
   476  			return r
   477  		}
   478  		glogBody("Request Body", data)
   479  		r.body = nil
   480  		r.bodyBytes = data
   481  		r.SetHeader("Content-Type", r.c.content.ContentType)
   482  	default:
   483  		r.err = fmt.Errorf("unknown type used for body: %+v", obj)
   484  	}
   485  	return r
   486  }
   487  
   488  // Error returns any error encountered constructing the request, if any.
   489  func (r *Request) Error() error {
   490  	return r.err
   491  }
   492  
   493  // URL returns the current working URL. Check the result of Error() to ensure
   494  // that the returned URL is valid.
   495  func (r *Request) URL() *url.URL {
   496  	p := r.pathPrefix
   497  	if r.namespaceSet && len(r.namespace) > 0 {
   498  		p = path.Join(p, "namespaces", r.namespace)
   499  	}
   500  	if len(r.resource) != 0 {
   501  		p = path.Join(p, strings.ToLower(r.resource))
   502  	}
   503  	// Join trims trailing slashes, so preserve r.pathPrefix's trailing slash for backwards compatibility if nothing was changed
   504  	if len(r.resourceName) != 0 || len(r.subpath) != 0 || len(r.subresource) != 0 {
   505  		p = path.Join(p, r.resourceName, r.subresource, r.subpath)
   506  	}
   507  
   508  	finalURL := &url.URL{}
   509  	if r.c.base != nil {
   510  		*finalURL = *r.c.base
   511  	}
   512  	finalURL.Path = p
   513  
   514  	query := url.Values{}
   515  	for key, values := range r.params {
   516  		for _, value := range values {
   517  			query.Add(key, value)
   518  		}
   519  	}
   520  
   521  	// timeout is handled specially here.
   522  	if r.timeout != 0 {
   523  		query.Set("timeout", r.timeout.String())
   524  	}
   525  	finalURL.RawQuery = query.Encode()
   526  	return finalURL
   527  }
   528  
   529  // finalURLTemplate is similar to URL(), but will make all specific parameter values equal
   530  // - instead of name or namespace, "{name}" and "{namespace}" will be used, and all query
   531  // parameters will be reset. This creates a copy of the url so as not to change the
   532  // underlying object.
   533  func (r Request) finalURLTemplate() url.URL {
   534  	newParams := url.Values{}
   535  	v := []string{"{value}"}
   536  	for k := range r.params {
   537  		newParams[k] = v
   538  	}
   539  	r.params = newParams
   540  	u := r.URL()
   541  	if u == nil {
   542  		return url.URL{}
   543  	}
   544  
   545  	segments := strings.Split(u.Path, "/")
   546  	groupIndex := 0
   547  	index := 0
   548  	trimmedBasePath := ""
   549  	if r.c.base != nil && strings.Contains(u.Path, r.c.base.Path) {
   550  		p := strings.TrimPrefix(u.Path, r.c.base.Path)
   551  		if !strings.HasPrefix(p, "/") {
   552  			p = "/" + p
   553  		}
   554  		// store the base path that we have trimmed so we can append it
   555  		// before returning the URL
   556  		trimmedBasePath = r.c.base.Path
   557  		segments = strings.Split(p, "/")
   558  		groupIndex = 1
   559  	}
   560  	if len(segments) <= 2 {
   561  		return *u
   562  	}
   563  
   564  	const CoreGroupPrefix = "api"
   565  	const NamedGroupPrefix = "apis"
   566  	isCoreGroup := segments[groupIndex] == CoreGroupPrefix
   567  	isNamedGroup := segments[groupIndex] == NamedGroupPrefix
   568  	if isCoreGroup {
   569  		// checking the case of core group with /api/v1/... format
   570  		index = groupIndex + 2
   571  	} else if isNamedGroup {
   572  		// checking the case of named group with /apis/apps/v1/... format
   573  		index = groupIndex + 3
   574  	} else {
   575  		// this should not happen that the only two possibilities are /api... and /apis..., just want to put an
   576  		// outlet here in case more API groups are added in future if ever possible:
   577  		// https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups
   578  		// if a wrong API groups name is encountered, return the {prefix} for url.Path
   579  		u.Path = "/{prefix}"
   580  		u.RawQuery = ""
   581  		return *u
   582  	}
   583  	// switch segLength := len(segments) - index; segLength {
   584  	switch {
   585  	// case len(segments) - index == 1:
   586  	// resource (with no name) do nothing
   587  	case len(segments)-index == 2:
   588  		// /$RESOURCE/$NAME: replace $NAME with {name}
   589  		segments[index+1] = "{name}"
   590  	case len(segments)-index == 3:
   591  		if segments[index+2] == "finalize" || segments[index+2] == "status" {
   592  			// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
   593  			segments[index+1] = "{name}"
   594  		} else {
   595  			// /namespace/$NAMESPACE/$RESOURCE: replace $NAMESPACE with {namespace}
   596  			segments[index+1] = "{namespace}"
   597  		}
   598  	case len(segments)-index >= 4:
   599  		segments[index+1] = "{namespace}"
   600  		// /namespace/$NAMESPACE/$RESOURCE/$NAME: replace $NAMESPACE with {namespace},  $NAME with {name}
   601  		if segments[index+3] != "finalize" && segments[index+3] != "status" {
   602  			// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
   603  			segments[index+3] = "{name}"
   604  		}
   605  	}
   606  	u.Path = path.Join(trimmedBasePath, path.Join(segments...))
   607  	return *u
   608  }
   609  
   610  func (r *Request) tryThrottleWithInfo(ctx context.Context, retryInfo string) error {
   611  	if r.rateLimiter == nil {
   612  		return nil
   613  	}
   614  
   615  	now := time.Now()
   616  
   617  	err := r.rateLimiter.Wait(ctx)
   618  	if err != nil {
   619  		err = fmt.Errorf("client rate limiter Wait returned an error: %w", err)
   620  	}
   621  	latency := time.Since(now)
   622  
   623  	var message string
   624  	switch {
   625  	case len(retryInfo) > 0:
   626  		message = fmt.Sprintf("Waited for %v, %s - request: %s:%s", latency, retryInfo, r.verb, r.URL().String())
   627  	default:
   628  		message = fmt.Sprintf("Waited for %v due to client-side throttling, not priority and fairness, request: %s:%s", latency, r.verb, r.URL().String())
   629  	}
   630  
   631  	if latency > longThrottleLatency {
   632  		klog.V(3).Info(message)
   633  	}
   634  	if latency > extraLongThrottleLatency {
   635  		// If the rate limiter latency is very high, the log message should be printed at a higher log level,
   636  		// but we use a throttled logger to prevent spamming.
   637  		globalThrottledLogger.Infof("%s", message)
   638  	}
   639  	metrics.RateLimiterLatency.Observe(ctx, r.verb, r.finalURLTemplate(), latency)
   640  
   641  	return err
   642  }
   643  
   644  func (r *Request) tryThrottle(ctx context.Context) error {
   645  	return r.tryThrottleWithInfo(ctx, "")
   646  }
   647  
   648  type throttleSettings struct {
   649  	logLevel       klog.Level
   650  	minLogInterval time.Duration
   651  
   652  	lastLogTime time.Time
   653  	lock        sync.RWMutex
   654  }
   655  
   656  type throttledLogger struct {
   657  	clock    clock.PassiveClock
   658  	settings []*throttleSettings
   659  }
   660  
   661  var globalThrottledLogger = &throttledLogger{
   662  	clock: clock.RealClock{},
   663  	settings: []*throttleSettings{
   664  		{
   665  			logLevel:       2,
   666  			minLogInterval: 1 * time.Second,
   667  		}, {
   668  			logLevel:       0,
   669  			minLogInterval: 10 * time.Second,
   670  		},
   671  	},
   672  }
   673  
   674  func (b *throttledLogger) attemptToLog() (klog.Level, bool) {
   675  	for _, setting := range b.settings {
   676  		if bool(klog.V(setting.logLevel).Enabled()) {
   677  			// Return early without write locking if possible.
   678  			if func() bool {
   679  				setting.lock.RLock()
   680  				defer setting.lock.RUnlock()
   681  				return b.clock.Since(setting.lastLogTime) >= setting.minLogInterval
   682  			}() {
   683  				setting.lock.Lock()
   684  				defer setting.lock.Unlock()
   685  				if b.clock.Since(setting.lastLogTime) >= setting.minLogInterval {
   686  					setting.lastLogTime = b.clock.Now()
   687  					return setting.logLevel, true
   688  				}
   689  			}
   690  			return -1, false
   691  		}
   692  	}
   693  	return -1, false
   694  }
   695  
   696  // Infof will write a log message at each logLevel specified by the receiver's throttleSettings
   697  // as long as it hasn't written a log message more recently than minLogInterval.
   698  func (b *throttledLogger) Infof(message string, args ...interface{}) {
   699  	if logLevel, ok := b.attemptToLog(); ok {
   700  		klog.V(logLevel).Infof(message, args...)
   701  	}
   702  }
   703  
   704  // Watch attempts to begin watching the requested location.
   705  // Returns a watch.Interface, or an error.
   706  func (r *Request) Watch(ctx context.Context) (watch.Interface, error) {
   707  	// We specifically don't want to rate limit watches, so we
   708  	// don't use r.rateLimiter here.
   709  	if r.err != nil {
   710  		return nil, r.err
   711  	}
   712  
   713  	client := r.c.Client
   714  	if client == nil {
   715  		client = http.DefaultClient
   716  	}
   717  
   718  	isErrRetryableFunc := func(request *http.Request, err error) bool {
   719  		// The watch stream mechanism handles many common partial data errors, so closed
   720  		// connections can be retried in many cases.
   721  		if net.IsProbableEOF(err) || net.IsTimeout(err) {
   722  			return true
   723  		}
   724  		return false
   725  	}
   726  	retry := r.retryFn(r.maxRetries)
   727  	url := r.URL().String()
   728  	for {
   729  		if err := retry.Before(ctx, r); err != nil {
   730  			return nil, retry.WrapPreviousError(err)
   731  		}
   732  
   733  		req, err := r.newHTTPRequest(ctx)
   734  		if err != nil {
   735  			return nil, err
   736  		}
   737  
   738  		resp, err := client.Do(req)
   739  		retry.After(ctx, r, resp, err)
   740  		if err == nil && resp.StatusCode == http.StatusOK {
   741  			return r.newStreamWatcher(resp)
   742  		}
   743  
   744  		done, transformErr := func() (bool, error) {
   745  			defer readAndCloseResponseBody(resp)
   746  
   747  			if retry.IsNextRetry(ctx, r, req, resp, err, isErrRetryableFunc) {
   748  				return false, nil
   749  			}
   750  
   751  			if resp == nil {
   752  				// the server must have sent us an error in 'err'
   753  				return true, nil
   754  			}
   755  			if result := r.transformResponse(resp, req); result.err != nil {
   756  				return true, result.err
   757  			}
   758  			return true, fmt.Errorf("for request %s, got status: %v", url, resp.StatusCode)
   759  		}()
   760  		if done {
   761  			if isErrRetryableFunc(req, err) {
   762  				return watch.NewEmptyWatch(), nil
   763  			}
   764  			if err == nil {
   765  				// if the server sent us an HTTP Response object,
   766  				// we need to return the error object from that.
   767  				err = transformErr
   768  			}
   769  			return nil, retry.WrapPreviousError(err)
   770  		}
   771  	}
   772  }
   773  
   774  type WatchListResult struct {
   775  	// err holds any errors we might have received
   776  	// during streaming.
   777  	err error
   778  
   779  	// items hold the collected data
   780  	items []runtime.Object
   781  
   782  	// initialEventsEndBookmarkRV holds the resource version
   783  	// extracted from the bookmark event that marks
   784  	// the end of the stream.
   785  	initialEventsEndBookmarkRV string
   786  
   787  	// gv represents the API version
   788  	// it is used to construct the final list response
   789  	// normally this information is filled by the server
   790  	gv schema.GroupVersion
   791  }
   792  
   793  func (r WatchListResult) Into(obj runtime.Object) error {
   794  	if r.err != nil {
   795  		return r.err
   796  	}
   797  
   798  	listPtr, err := meta.GetItemsPtr(obj)
   799  	if err != nil {
   800  		return err
   801  	}
   802  	listVal, err := conversion.EnforcePtr(listPtr)
   803  	if err != nil {
   804  		return err
   805  	}
   806  	if listVal.Kind() != reflect.Slice {
   807  		return fmt.Errorf("need a pointer to slice, got %v", listVal.Kind())
   808  	}
   809  
   810  	if len(r.items) == 0 {
   811  		listVal.Set(reflect.MakeSlice(listVal.Type(), 0, 0))
   812  	} else {
   813  		listVal.Set(reflect.MakeSlice(listVal.Type(), len(r.items), len(r.items)))
   814  		for i, o := range r.items {
   815  			if listVal.Type().Elem() != reflect.TypeOf(o).Elem() {
   816  				return fmt.Errorf("received object type = %v at index = %d, doesn't match the list item type = %v", reflect.TypeOf(o).Elem(), i, listVal.Type().Elem())
   817  			}
   818  			listVal.Index(i).Set(reflect.ValueOf(o).Elem())
   819  		}
   820  	}
   821  
   822  	listMeta, err := meta.ListAccessor(obj)
   823  	if err != nil {
   824  		return err
   825  	}
   826  	listMeta.SetResourceVersion(r.initialEventsEndBookmarkRV)
   827  
   828  	typeMeta, err := meta.TypeAccessor(obj)
   829  	if err != nil {
   830  		return err
   831  	}
   832  	version := r.gv.String()
   833  	typeMeta.SetAPIVersion(version)
   834  	typeMeta.SetKind(reflect.TypeOf(obj).Elem().Name())
   835  
   836  	return nil
   837  }
   838  
   839  // WatchList establishes a stream to get a consistent snapshot of data
   840  // from the server as described in https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/3157-watch-list#proposal
   841  //
   842  // Note that the watchlist requires properly setting the ListOptions
   843  // otherwise it just establishes a regular watch with the server.
   844  // Check the documentation https://kubernetes.io/docs/reference/using-api/api-concepts/#streaming-lists
   845  // to see what parameters are currently required.
   846  func (r *Request) WatchList(ctx context.Context) WatchListResult {
   847  	if !clientfeatures.FeatureGates().Enabled(clientfeatures.WatchListClient) {
   848  		return WatchListResult{err: fmt.Errorf("%q feature gate is not enabled", clientfeatures.WatchListClient)}
   849  	}
   850  	// TODO(#115478): consider validating request parameters (i.e sendInitialEvents).
   851  	//  Most users use the generated client, which handles the proper setting of parameters.
   852  	//  We don't have validation for other methods (e.g., the Watch)
   853  	//  thus, for symmetry, we haven't added additional checks for the WatchList method.
   854  	w, err := r.Watch(ctx)
   855  	if err != nil {
   856  		return WatchListResult{err: err}
   857  	}
   858  	return r.handleWatchList(ctx, w)
   859  }
   860  
   861  // handleWatchList holds the actual logic for easier unit testing.
   862  // Note that this function will close the passed watch.
   863  func (r *Request) handleWatchList(ctx context.Context, w watch.Interface) WatchListResult {
   864  	defer w.Stop()
   865  	var lastKey string
   866  	var items []runtime.Object
   867  
   868  	for {
   869  		select {
   870  		case <-ctx.Done():
   871  			return WatchListResult{err: ctx.Err()}
   872  		case event, ok := <-w.ResultChan():
   873  			if !ok {
   874  				return WatchListResult{err: fmt.Errorf("unexpected watch close")}
   875  			}
   876  			if event.Type == watch.Error {
   877  				return WatchListResult{err: errors.FromObject(event.Object)}
   878  			}
   879  			meta, err := meta.Accessor(event.Object)
   880  			if err != nil {
   881  				return WatchListResult{err: fmt.Errorf("failed to parse watch event: %#v", event)}
   882  			}
   883  
   884  			switch event.Type {
   885  			case watch.Added:
   886  				// the following check ensures that the response is ordered.
   887  				// earlier servers had a bug that caused them to not sort the output.
   888  				// in such cases, return an error which can trigger fallback logic.
   889  				key := objectKeyFromMeta(meta)
   890  				if len(lastKey) > 0 && lastKey > key {
   891  					return WatchListResult{err: fmt.Errorf("cannot add the obj (%#v) with the key = %s, as it violates the ordering guarantees provided by the watchlist feature in beta phase, lastInsertedKey was = %s", event.Object, key, lastKey)}
   892  				}
   893  				items = append(items, event.Object)
   894  				lastKey = key
   895  			case watch.Bookmark:
   896  				if meta.GetAnnotations()[metav1.InitialEventsAnnotationKey] == "true" {
   897  					return WatchListResult{
   898  						items:                      items,
   899  						initialEventsEndBookmarkRV: meta.GetResourceVersion(),
   900  						gv:                         r.c.content.GroupVersion,
   901  					}
   902  				}
   903  			default:
   904  				return WatchListResult{err: fmt.Errorf("unexpected watch event %#v, expected to only receive watch.Added and watch.Bookmark events", event)}
   905  			}
   906  		}
   907  	}
   908  }
   909  
   910  func (r *Request) newStreamWatcher(resp *http.Response) (watch.Interface, error) {
   911  	contentType := resp.Header.Get("Content-Type")
   912  	mediaType, params, err := mime.ParseMediaType(contentType)
   913  	if err != nil {
   914  		klog.V(4).Infof("Unexpected content type from the server: %q: %v", contentType, err)
   915  	}
   916  	objectDecoder, streamingSerializer, framer, err := r.c.content.Negotiator.StreamDecoder(mediaType, params)
   917  	if err != nil {
   918  		return nil, err
   919  	}
   920  
   921  	handleWarnings(resp.Header, r.warningHandler)
   922  
   923  	frameReader := framer.NewFrameReader(resp.Body)
   924  	watchEventDecoder := streaming.NewDecoder(frameReader, streamingSerializer)
   925  
   926  	return watch.NewStreamWatcher(
   927  		restclientwatch.NewDecoder(watchEventDecoder, objectDecoder),
   928  		// use 500 to indicate that the cause of the error is unknown - other error codes
   929  		// are more specific to HTTP interactions, and set a reason
   930  		errors.NewClientErrorReporter(http.StatusInternalServerError, r.verb, "ClientWatchDecoding"),
   931  	), nil
   932  }
   933  
   934  // updateRequestResultMetric increments the RequestResult metric counter,
   935  // it should be called with the (response, err) tuple from the final
   936  // reply from the server.
   937  func updateRequestResultMetric(ctx context.Context, req *Request, resp *http.Response, err error) {
   938  	code, host := sanitize(req, resp, err)
   939  	metrics.RequestResult.Increment(ctx, code, req.verb, host)
   940  }
   941  
   942  // updateRequestRetryMetric increments the RequestRetry metric counter,
   943  // it should be called with the (response, err) tuple for each retry
   944  // except for the final attempt.
   945  func updateRequestRetryMetric(ctx context.Context, req *Request, resp *http.Response, err error) {
   946  	code, host := sanitize(req, resp, err)
   947  	metrics.RequestRetry.IncrementRetry(ctx, code, req.verb, host)
   948  }
   949  
   950  func sanitize(req *Request, resp *http.Response, err error) (string, string) {
   951  	host := "none"
   952  	if req.c.base != nil {
   953  		host = req.c.base.Host
   954  	}
   955  
   956  	// Errors can be arbitrary strings. Unbound label cardinality is not suitable for a metric
   957  	// system so we just report them as `<error>`.
   958  	code := "<error>"
   959  	if resp != nil {
   960  		code = strconv.Itoa(resp.StatusCode)
   961  	}
   962  
   963  	return code, host
   964  }
   965  
   966  // Stream formats and executes the request, and offers streaming of the response.
   967  // Returns io.ReadCloser which could be used for streaming of the response, or an error
   968  // Any non-2xx http status code causes an error.  If we get a non-2xx code, we try to convert the body into an APIStatus object.
   969  // If we can, we return that as an error.  Otherwise, we create an error that lists the http status and the content of the response.
   970  func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
   971  	if r.err != nil {
   972  		return nil, r.err
   973  	}
   974  
   975  	if err := r.tryThrottle(ctx); err != nil {
   976  		return nil, err
   977  	}
   978  
   979  	client := r.c.Client
   980  	if client == nil {
   981  		client = http.DefaultClient
   982  	}
   983  
   984  	retry := r.retryFn(r.maxRetries)
   985  	url := r.URL().String()
   986  	for {
   987  		if err := retry.Before(ctx, r); err != nil {
   988  			return nil, err
   989  		}
   990  
   991  		req, err := r.newHTTPRequest(ctx)
   992  		if err != nil {
   993  			return nil, err
   994  		}
   995  		resp, err := client.Do(req)
   996  		retry.After(ctx, r, resp, err)
   997  		if err != nil {
   998  			// we only retry on an HTTP response with 'Retry-After' header
   999  			return nil, err
  1000  		}
  1001  
  1002  		switch {
  1003  		case (resp.StatusCode >= 200) && (resp.StatusCode < 300):
  1004  			handleWarnings(resp.Header, r.warningHandler)
  1005  			return resp.Body, nil
  1006  
  1007  		default:
  1008  			done, transformErr := func() (bool, error) {
  1009  				defer resp.Body.Close()
  1010  
  1011  				if retry.IsNextRetry(ctx, r, req, resp, err, neverRetryError) {
  1012  					return false, nil
  1013  				}
  1014  				result := r.transformResponse(resp, req)
  1015  				if err := result.Error(); err != nil {
  1016  					return true, err
  1017  				}
  1018  				return true, fmt.Errorf("%d while accessing %v: %s", result.statusCode, url, string(result.body))
  1019  			}()
  1020  			if done {
  1021  				return nil, transformErr
  1022  			}
  1023  		}
  1024  	}
  1025  }
  1026  
  1027  // requestPreflightCheck looks for common programmer errors on Request.
  1028  //
  1029  // We tackle here two programmer mistakes. The first one is to try to create
  1030  // something(POST) using an empty string as namespace with namespaceSet as
  1031  // true. If namespaceSet is true then namespace should also be defined. The
  1032  // second mistake is, when under the same circumstances, the programmer tries
  1033  // to GET, PUT or DELETE a named resource(resourceName != ""), again, if
  1034  // namespaceSet is true then namespace must not be empty.
  1035  func (r *Request) requestPreflightCheck() error {
  1036  	if !r.namespaceSet {
  1037  		return nil
  1038  	}
  1039  	if len(r.namespace) > 0 {
  1040  		return nil
  1041  	}
  1042  
  1043  	switch r.verb {
  1044  	case "POST":
  1045  		return fmt.Errorf("an empty namespace may not be set during creation")
  1046  	case "GET", "PUT", "DELETE":
  1047  		if len(r.resourceName) > 0 {
  1048  			return fmt.Errorf("an empty namespace may not be set when a resource name is provided")
  1049  		}
  1050  	}
  1051  	return nil
  1052  }
  1053  
  1054  func (r *Request) newHTTPRequest(ctx context.Context) (*http.Request, error) {
  1055  	var body io.Reader
  1056  	switch {
  1057  	case r.body != nil && r.bodyBytes != nil:
  1058  		return nil, fmt.Errorf("cannot set both body and bodyBytes")
  1059  	case r.body != nil:
  1060  		body = r.body
  1061  	case r.bodyBytes != nil:
  1062  		// Create a new reader specifically for this request.
  1063  		// Giving each request a dedicated reader allows retries to avoid races resetting the request body.
  1064  		body = bytes.NewReader(r.bodyBytes)
  1065  	}
  1066  
  1067  	url := r.URL().String()
  1068  	req, err := http.NewRequestWithContext(httptrace.WithClientTrace(ctx, newDNSMetricsTrace(ctx)), r.verb, url, body)
  1069  	if err != nil {
  1070  		return nil, err
  1071  	}
  1072  	req.Header = r.headers
  1073  	return req, nil
  1074  }
  1075  
  1076  // newDNSMetricsTrace returns an HTTP trace that tracks time spent on DNS lookups per host.
  1077  // This metric is available in client as "rest_client_dns_resolution_duration_seconds".
  1078  func newDNSMetricsTrace(ctx context.Context) *httptrace.ClientTrace {
  1079  	type dnsMetric struct {
  1080  		start time.Time
  1081  		host  string
  1082  		sync.Mutex
  1083  	}
  1084  	dns := &dnsMetric{}
  1085  	return &httptrace.ClientTrace{
  1086  		DNSStart: func(info httptrace.DNSStartInfo) {
  1087  			dns.Lock()
  1088  			defer dns.Unlock()
  1089  			dns.start = time.Now()
  1090  			dns.host = info.Host
  1091  		},
  1092  		DNSDone: func(info httptrace.DNSDoneInfo) {
  1093  			dns.Lock()
  1094  			defer dns.Unlock()
  1095  			metrics.ResolverLatency.Observe(ctx, dns.host, time.Since(dns.start))
  1096  		},
  1097  	}
  1098  }
  1099  
  1100  // request connects to the server and invokes the provided function when a server response is
  1101  // received. It handles retry behavior and up front validation of requests. It will invoke
  1102  // fn at most once. It will return an error if a problem occurred prior to connecting to the
  1103  // server - the provided function is responsible for handling server errors.
  1104  func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Response)) error {
  1105  	// Metrics for total request latency
  1106  	start := time.Now()
  1107  	defer func() {
  1108  		metrics.RequestLatency.Observe(ctx, r.verb, r.finalURLTemplate(), time.Since(start))
  1109  	}()
  1110  
  1111  	if r.err != nil {
  1112  		klog.V(4).Infof("Error in request: %v", r.err)
  1113  		return r.err
  1114  	}
  1115  
  1116  	if err := r.requestPreflightCheck(); err != nil {
  1117  		return err
  1118  	}
  1119  
  1120  	client := r.c.Client
  1121  	if client == nil {
  1122  		client = http.DefaultClient
  1123  	}
  1124  
  1125  	// Throttle the first try before setting up the timeout configured on the
  1126  	// client. We don't want a throttled client to return timeouts to callers
  1127  	// before it makes a single request.
  1128  	if err := r.tryThrottle(ctx); err != nil {
  1129  		return err
  1130  	}
  1131  
  1132  	if r.timeout > 0 {
  1133  		var cancel context.CancelFunc
  1134  		ctx, cancel = context.WithTimeout(ctx, r.timeout)
  1135  		defer cancel()
  1136  	}
  1137  
  1138  	isErrRetryableFunc := func(req *http.Request, err error) bool {
  1139  		// "Connection reset by peer" or "apiserver is shutting down" are usually a transient errors.
  1140  		// Thus in case of "GET" operations, we simply retry it.
  1141  		// We are not automatically retrying "write" operations, as they are not idempotent.
  1142  		if req.Method != "GET" {
  1143  			return false
  1144  		}
  1145  		// For connection errors and apiserver shutdown errors retry.
  1146  		if net.IsConnectionReset(err) || net.IsProbableEOF(err) {
  1147  			return true
  1148  		}
  1149  		return false
  1150  	}
  1151  
  1152  	// Right now we make about ten retry attempts if we get a Retry-After response.
  1153  	retry := r.retryFn(r.maxRetries)
  1154  	for {
  1155  		if err := retry.Before(ctx, r); err != nil {
  1156  			return retry.WrapPreviousError(err)
  1157  		}
  1158  		req, err := r.newHTTPRequest(ctx)
  1159  		if err != nil {
  1160  			return err
  1161  		}
  1162  		resp, err := client.Do(req)
  1163  		// The value -1 or a value of 0 with a non-nil Body indicates that the length is unknown.
  1164  		// https://pkg.go.dev/net/http#Request
  1165  		if req.ContentLength >= 0 && !(req.Body != nil && req.ContentLength == 0) {
  1166  			metrics.RequestSize.Observe(ctx, r.verb, r.URL().Host, float64(req.ContentLength))
  1167  		}
  1168  		retry.After(ctx, r, resp, err)
  1169  
  1170  		done := func() bool {
  1171  			defer readAndCloseResponseBody(resp)
  1172  
  1173  			// if the server returns an error in err, the response will be nil.
  1174  			f := func(req *http.Request, resp *http.Response) {
  1175  				if resp == nil {
  1176  					return
  1177  				}
  1178  				fn(req, resp)
  1179  			}
  1180  
  1181  			if retry.IsNextRetry(ctx, r, req, resp, err, isErrRetryableFunc) {
  1182  				return false
  1183  			}
  1184  
  1185  			f(req, resp)
  1186  			return true
  1187  		}()
  1188  		if done {
  1189  			return retry.WrapPreviousError(err)
  1190  		}
  1191  	}
  1192  }
  1193  
  1194  // Do formats and executes the request. Returns a Result object for easy response
  1195  // processing.
  1196  //
  1197  // Error type:
  1198  //   - If the server responds with a status: *errors.StatusError or *errors.UnexpectedObjectError
  1199  //   - http.Client.Do errors are returned directly.
  1200  func (r *Request) Do(ctx context.Context) Result {
  1201  	var result Result
  1202  	err := r.request(ctx, func(req *http.Request, resp *http.Response) {
  1203  		result = r.transformResponse(resp, req)
  1204  	})
  1205  	if err != nil {
  1206  		return Result{err: err}
  1207  	}
  1208  	if result.err == nil || len(result.body) > 0 {
  1209  		metrics.ResponseSize.Observe(ctx, r.verb, r.URL().Host, float64(len(result.body)))
  1210  	}
  1211  	return result
  1212  }
  1213  
  1214  // DoRaw executes the request but does not process the response body.
  1215  func (r *Request) DoRaw(ctx context.Context) ([]byte, error) {
  1216  	var result Result
  1217  	err := r.request(ctx, func(req *http.Request, resp *http.Response) {
  1218  		result.body, result.err = io.ReadAll(resp.Body)
  1219  		glogBody("Response Body", result.body)
  1220  		if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent {
  1221  			result.err = r.transformUnstructuredResponseError(resp, req, result.body)
  1222  		}
  1223  	})
  1224  	if err != nil {
  1225  		return nil, err
  1226  	}
  1227  	if result.err == nil || len(result.body) > 0 {
  1228  		metrics.ResponseSize.Observe(ctx, r.verb, r.URL().Host, float64(len(result.body)))
  1229  	}
  1230  	return result.body, result.err
  1231  }
  1232  
  1233  // transformResponse converts an API response into a structured API object
  1234  func (r *Request) transformResponse(resp *http.Response, req *http.Request) Result {
  1235  	var body []byte
  1236  	if resp.Body != nil {
  1237  		data, err := io.ReadAll(resp.Body)
  1238  		switch err.(type) {
  1239  		case nil:
  1240  			body = data
  1241  		case http2.StreamError:
  1242  			// This is trying to catch the scenario that the server may close the connection when sending the
  1243  			// response body. This can be caused by server timeout due to a slow network connection.
  1244  			// TODO: Add test for this. Steps may be:
  1245  			// 1. client-go (or kubectl) sends a GET request.
  1246  			// 2. Apiserver sends back the headers and then part of the body
  1247  			// 3. Apiserver closes connection.
  1248  			// 4. client-go should catch this and return an error.
  1249  			klog.V(2).Infof("Stream error %#v when reading response body, may be caused by closed connection.", err)
  1250  			streamErr := fmt.Errorf("stream error when reading response body, may be caused by closed connection. Please retry. Original error: %w", err)
  1251  			return Result{
  1252  				err: streamErr,
  1253  			}
  1254  		default:
  1255  			klog.Errorf("Unexpected error when reading response body: %v", err)
  1256  			unexpectedErr := fmt.Errorf("unexpected error when reading response body. Please retry. Original error: %w", err)
  1257  			return Result{
  1258  				err: unexpectedErr,
  1259  			}
  1260  		}
  1261  	}
  1262  
  1263  	glogBody("Response Body", body)
  1264  
  1265  	// verify the content type is accurate
  1266  	var decoder runtime.Decoder
  1267  	contentType := resp.Header.Get("Content-Type")
  1268  	if len(contentType) == 0 {
  1269  		contentType = r.c.content.ContentType
  1270  	}
  1271  	if len(contentType) > 0 {
  1272  		var err error
  1273  		mediaType, params, err := mime.ParseMediaType(contentType)
  1274  		if err != nil {
  1275  			return Result{err: errors.NewInternalError(err)}
  1276  		}
  1277  		decoder, err = r.c.content.Negotiator.Decoder(mediaType, params)
  1278  		if err != nil {
  1279  			// if we fail to negotiate a decoder, treat this as an unstructured error
  1280  			switch {
  1281  			case resp.StatusCode == http.StatusSwitchingProtocols:
  1282  				// no-op, we've been upgraded
  1283  			case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent:
  1284  				return Result{err: r.transformUnstructuredResponseError(resp, req, body)}
  1285  			}
  1286  			return Result{
  1287  				body:        body,
  1288  				contentType: contentType,
  1289  				statusCode:  resp.StatusCode,
  1290  				warnings:    handleWarnings(resp.Header, r.warningHandler),
  1291  			}
  1292  		}
  1293  	}
  1294  
  1295  	switch {
  1296  	case resp.StatusCode == http.StatusSwitchingProtocols:
  1297  		// no-op, we've been upgraded
  1298  	case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent:
  1299  		// calculate an unstructured error from the response which the Result object may use if the caller
  1300  		// did not return a structured error.
  1301  		retryAfter, _ := retryAfterSeconds(resp)
  1302  		err := r.newUnstructuredResponseError(body, isTextResponse(resp), resp.StatusCode, req.Method, retryAfter)
  1303  		return Result{
  1304  			body:        body,
  1305  			contentType: contentType,
  1306  			statusCode:  resp.StatusCode,
  1307  			decoder:     decoder,
  1308  			err:         err,
  1309  			warnings:    handleWarnings(resp.Header, r.warningHandler),
  1310  		}
  1311  	}
  1312  
  1313  	return Result{
  1314  		body:        body,
  1315  		contentType: contentType,
  1316  		statusCode:  resp.StatusCode,
  1317  		decoder:     decoder,
  1318  		warnings:    handleWarnings(resp.Header, r.warningHandler),
  1319  	}
  1320  }
  1321  
  1322  // truncateBody decides if the body should be truncated, based on the glog Verbosity.
  1323  func truncateBody(body string) string {
  1324  	max := 0
  1325  	switch {
  1326  	case bool(klog.V(10).Enabled()):
  1327  		return body
  1328  	case bool(klog.V(9).Enabled()):
  1329  		max = 10240
  1330  	case bool(klog.V(8).Enabled()):
  1331  		max = 1024
  1332  	}
  1333  
  1334  	if len(body) <= max {
  1335  		return body
  1336  	}
  1337  
  1338  	return body[:max] + fmt.Sprintf(" [truncated %d chars]", len(body)-max)
  1339  }
  1340  
  1341  // glogBody logs a body output that could be either JSON or protobuf. It explicitly guards against
  1342  // allocating a new string for the body output unless necessary. Uses a simple heuristic to determine
  1343  // whether the body is printable.
  1344  func glogBody(prefix string, body []byte) {
  1345  	if klogV := klog.V(8); klogV.Enabled() {
  1346  		if bytes.IndexFunc(body, func(r rune) bool {
  1347  			return r < 0x0a
  1348  		}) != -1 {
  1349  			klogV.Infof("%s:\n%s", prefix, truncateBody(hex.Dump(body)))
  1350  		} else {
  1351  			klogV.Infof("%s: %s", prefix, truncateBody(string(body)))
  1352  		}
  1353  	}
  1354  }
  1355  
  1356  // maxUnstructuredResponseTextBytes is an upper bound on how much output to include in the unstructured error.
  1357  const maxUnstructuredResponseTextBytes = 2048
  1358  
  1359  // transformUnstructuredResponseError handles an error from the server that is not in a structured form.
  1360  // It is expected to transform any response that is not recognizable as a clear server sent error from the
  1361  // K8S API using the information provided with the request. In practice, HTTP proxies and client libraries
  1362  // introduce a level of uncertainty to the responses returned by servers that in common use result in
  1363  // unexpected responses. The rough structure is:
  1364  //
  1365  // 1. Assume the server sends you something sane - JSON + well defined error objects + proper codes
  1366  //   - this is the happy path
  1367  //   - when you get this output, trust what the server sends
  1368  //     2. Guard against empty fields / bodies in received JSON and attempt to cull sufficient info from them to
  1369  //     generate a reasonable facsimile of the original failure.
  1370  //   - Be sure to use a distinct error type or flag that allows a client to distinguish between this and error 1 above
  1371  //     3. Handle true disconnect failures / completely malformed data by moving up to a more generic client error
  1372  //     4. Distinguish between various connection failures like SSL certificates, timeouts, proxy errors, unexpected
  1373  //     initial contact, the presence of mismatched body contents from posted content types
  1374  //   - Give these a separate distinct error type and capture as much as possible of the original message
  1375  //
  1376  // TODO: introduce transformation of generic http.Client.Do() errors that separates 4.
  1377  func (r *Request) transformUnstructuredResponseError(resp *http.Response, req *http.Request, body []byte) error {
  1378  	if body == nil && resp.Body != nil {
  1379  		if data, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: maxUnstructuredResponseTextBytes}); err == nil {
  1380  			body = data
  1381  		}
  1382  	}
  1383  	retryAfter, _ := retryAfterSeconds(resp)
  1384  	return r.newUnstructuredResponseError(body, isTextResponse(resp), resp.StatusCode, req.Method, retryAfter)
  1385  }
  1386  
  1387  // newUnstructuredResponseError instantiates the appropriate generic error for the provided input. It also logs the body.
  1388  func (r *Request) newUnstructuredResponseError(body []byte, isTextResponse bool, statusCode int, method string, retryAfter int) error {
  1389  	// cap the amount of output we create
  1390  	if len(body) > maxUnstructuredResponseTextBytes {
  1391  		body = body[:maxUnstructuredResponseTextBytes]
  1392  	}
  1393  
  1394  	message := "unknown"
  1395  	if isTextResponse {
  1396  		message = strings.TrimSpace(string(body))
  1397  	}
  1398  	var groupResource schema.GroupResource
  1399  	if len(r.resource) > 0 {
  1400  		groupResource.Group = r.c.content.GroupVersion.Group
  1401  		groupResource.Resource = r.resource
  1402  	}
  1403  	return errors.NewGenericServerResponse(
  1404  		statusCode,
  1405  		method,
  1406  		groupResource,
  1407  		r.resourceName,
  1408  		message,
  1409  		retryAfter,
  1410  		true,
  1411  	)
  1412  }
  1413  
  1414  // isTextResponse returns true if the response appears to be a textual media type.
  1415  func isTextResponse(resp *http.Response) bool {
  1416  	contentType := resp.Header.Get("Content-Type")
  1417  	if len(contentType) == 0 {
  1418  		return true
  1419  	}
  1420  	media, _, err := mime.ParseMediaType(contentType)
  1421  	if err != nil {
  1422  		return false
  1423  	}
  1424  	return strings.HasPrefix(media, "text/")
  1425  }
  1426  
  1427  // retryAfterSeconds returns the value of the Retry-After header and true, or 0 and false if
  1428  // the header was missing or not a valid number.
  1429  func retryAfterSeconds(resp *http.Response) (int, bool) {
  1430  	if h := resp.Header.Get("Retry-After"); len(h) > 0 {
  1431  		if i, err := strconv.Atoi(h); err == nil {
  1432  			return i, true
  1433  		}
  1434  	}
  1435  	return 0, false
  1436  }
  1437  
  1438  // Result contains the result of calling Request.Do().
  1439  type Result struct {
  1440  	body        []byte
  1441  	warnings    []net.WarningHeader
  1442  	contentType string
  1443  	err         error
  1444  	statusCode  int
  1445  
  1446  	decoder runtime.Decoder
  1447  }
  1448  
  1449  // Raw returns the raw result.
  1450  func (r Result) Raw() ([]byte, error) {
  1451  	return r.body, r.err
  1452  }
  1453  
  1454  // Get returns the result as an object, which means it passes through the decoder.
  1455  // If the returned object is of type Status and has .Status != StatusSuccess, the
  1456  // additional information in Status will be used to enrich the error.
  1457  func (r Result) Get() (runtime.Object, error) {
  1458  	if r.err != nil {
  1459  		// Check whether the result has a Status object in the body and prefer that.
  1460  		return nil, r.Error()
  1461  	}
  1462  	if r.decoder == nil {
  1463  		return nil, fmt.Errorf("serializer for %s doesn't exist", r.contentType)
  1464  	}
  1465  
  1466  	// decode, but if the result is Status return that as an error instead.
  1467  	out, _, err := r.decoder.Decode(r.body, nil, nil)
  1468  	if err != nil {
  1469  		return nil, err
  1470  	}
  1471  	switch t := out.(type) {
  1472  	case *metav1.Status:
  1473  		// any status besides StatusSuccess is considered an error.
  1474  		if t.Status != metav1.StatusSuccess {
  1475  			return nil, errors.FromObject(t)
  1476  		}
  1477  	}
  1478  	return out, nil
  1479  }
  1480  
  1481  // StatusCode returns the HTTP status code of the request. (Only valid if no
  1482  // error was returned.)
  1483  func (r Result) StatusCode(statusCode *int) Result {
  1484  	*statusCode = r.statusCode
  1485  	return r
  1486  }
  1487  
  1488  // ContentType returns the "Content-Type" response header into the passed
  1489  // string, returning the Result for possible chaining. (Only valid if no
  1490  // error code was returned.)
  1491  func (r Result) ContentType(contentType *string) Result {
  1492  	*contentType = r.contentType
  1493  	return r
  1494  }
  1495  
  1496  // Into stores the result into obj, if possible. If obj is nil it is ignored.
  1497  // If the returned object is of type Status and has .Status != StatusSuccess, the
  1498  // additional information in Status will be used to enrich the error.
  1499  func (r Result) Into(obj runtime.Object) error {
  1500  	if r.err != nil {
  1501  		// Check whether the result has a Status object in the body and prefer that.
  1502  		return r.Error()
  1503  	}
  1504  	if r.decoder == nil {
  1505  		return fmt.Errorf("serializer for %s doesn't exist", r.contentType)
  1506  	}
  1507  	if len(r.body) == 0 {
  1508  		return fmt.Errorf("0-length response with status code: %d and content type: %s",
  1509  			r.statusCode, r.contentType)
  1510  	}
  1511  
  1512  	out, _, err := r.decoder.Decode(r.body, nil, obj)
  1513  	if err != nil || out == obj {
  1514  		return err
  1515  	}
  1516  	// if a different object is returned, see if it is Status and avoid double decoding
  1517  	// the object.
  1518  	switch t := out.(type) {
  1519  	case *metav1.Status:
  1520  		// any status besides StatusSuccess is considered an error.
  1521  		if t.Status != metav1.StatusSuccess {
  1522  			return errors.FromObject(t)
  1523  		}
  1524  	}
  1525  	return nil
  1526  }
  1527  
  1528  // WasCreated updates the provided bool pointer to whether the server returned
  1529  // 201 created or a different response.
  1530  func (r Result) WasCreated(wasCreated *bool) Result {
  1531  	*wasCreated = r.statusCode == http.StatusCreated
  1532  	return r
  1533  }
  1534  
  1535  // Error returns the error executing the request, nil if no error occurred.
  1536  // If the returned object is of type Status and has Status != StatusSuccess, the
  1537  // additional information in Status will be used to enrich the error.
  1538  // See the Request.Do() comment for what errors you might get.
  1539  func (r Result) Error() error {
  1540  	// if we have received an unexpected server error, and we have a body and decoder, we can try to extract
  1541  	// a Status object.
  1542  	if r.err == nil || !errors.IsUnexpectedServerError(r.err) || len(r.body) == 0 || r.decoder == nil {
  1543  		return r.err
  1544  	}
  1545  
  1546  	// attempt to convert the body into a Status object
  1547  	// to be backwards compatible with old servers that do not return a version, default to "v1"
  1548  	out, _, err := r.decoder.Decode(r.body, &schema.GroupVersionKind{Version: "v1"}, nil)
  1549  	if err != nil {
  1550  		klog.V(5).Infof("body was not decodable (unable to check for Status): %v", err)
  1551  		return r.err
  1552  	}
  1553  	switch t := out.(type) {
  1554  	case *metav1.Status:
  1555  		// because we default the kind, we *must* check for StatusFailure
  1556  		if t.Status == metav1.StatusFailure {
  1557  			return errors.FromObject(t)
  1558  		}
  1559  	}
  1560  	return r.err
  1561  }
  1562  
  1563  // Warnings returns any warning headers received in the response
  1564  func (r Result) Warnings() []net.WarningHeader {
  1565  	return r.warnings
  1566  }
  1567  
  1568  // NameMayNotBe specifies strings that cannot be used as names specified as path segments (like the REST API or etcd store)
  1569  var NameMayNotBe = []string{".", ".."}
  1570  
  1571  // NameMayNotContain specifies substrings that cannot be used in names specified as path segments (like the REST API or etcd store)
  1572  var NameMayNotContain = []string{"/", "%"}
  1573  
  1574  // IsValidPathSegmentName validates the name can be safely encoded as a path segment
  1575  func IsValidPathSegmentName(name string) []string {
  1576  	for _, illegalName := range NameMayNotBe {
  1577  		if name == illegalName {
  1578  			return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
  1579  		}
  1580  	}
  1581  
  1582  	var errors []string
  1583  	for _, illegalContent := range NameMayNotContain {
  1584  		if strings.Contains(name, illegalContent) {
  1585  			errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
  1586  		}
  1587  	}
  1588  
  1589  	return errors
  1590  }
  1591  
  1592  // IsValidPathSegmentPrefix validates the name can be used as a prefix for a name which will be encoded as a path segment
  1593  // It does not check for exact matches with disallowed names, since an arbitrary suffix might make the name valid
  1594  func IsValidPathSegmentPrefix(name string) []string {
  1595  	var errors []string
  1596  	for _, illegalContent := range NameMayNotContain {
  1597  		if strings.Contains(name, illegalContent) {
  1598  			errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
  1599  		}
  1600  	}
  1601  
  1602  	return errors
  1603  }
  1604  
  1605  // ValidatePathSegmentName validates the name can be safely encoded as a path segment
  1606  func ValidatePathSegmentName(name string, prefix bool) []string {
  1607  	if prefix {
  1608  		return IsValidPathSegmentPrefix(name)
  1609  	}
  1610  	return IsValidPathSegmentName(name)
  1611  }
  1612  
  1613  func objectKeyFromMeta(objMeta metav1.Object) string {
  1614  	if len(objMeta.GetNamespace()) > 0 {
  1615  		return fmt.Sprintf("%s/%s", objMeta.GetNamespace(), objMeta.GetName())
  1616  	}
  1617  	return objMeta.GetName()
  1618  }