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