build: move e2e dependencies into e2e/go.mod

Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos
2025-03-04 08:57:28 +01:00
committed by mergify[bot]
parent 15da101b1b
commit bec6090996
8047 changed files with 1407827 additions and 3453 deletions

14
e2e/vendor/k8s.io/client-go/rest/OWNERS generated vendored Normal file
View File

@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- thockin
- smarterclayton
- caesarxuchao
- wojtek-t
- deads2k
- liggitt
- sttts
- luxas
- dims
- cjcullen
- lojies

343
e2e/vendor/k8s.io/client-go/rest/client.go generated vendored Normal file
View File

@ -0,0 +1,343 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"mime"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/munnerz/goautoneg"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
clientfeatures "k8s.io/client-go/features"
"k8s.io/client-go/util/flowcontrol"
)
const (
// Environment variables: Note that the duration should be long enough that the backoff
// persists for some reasonable time (i.e. 120 seconds). The typical base might be "1".
envBackoffBase = "KUBE_CLIENT_BACKOFF_BASE"
envBackoffDuration = "KUBE_CLIENT_BACKOFF_DURATION"
)
// Interface captures the set of operations for generically interacting with Kubernetes REST apis.
type Interface interface {
GetRateLimiter() flowcontrol.RateLimiter
Verb(verb string) *Request
Post() *Request
Put() *Request
Patch(pt types.PatchType) *Request
Get() *Request
Delete() *Request
APIVersion() schema.GroupVersion
}
// ClientContentConfig controls how RESTClient communicates with the server.
//
// TODO: ContentConfig will be updated to accept a Negotiator instead of a
// NegotiatedSerializer and NegotiatedSerializer will be removed.
type ClientContentConfig struct {
// AcceptContentTypes specifies the types the client will accept and is optional.
// If not set, ContentType will be used to define the Accept header
AcceptContentTypes string
// ContentType specifies the wire format used to communicate with the server.
// This value will be set as the Accept header on requests made to the server if
// AcceptContentTypes is not set, and as the default content type on any object
// sent to the server. If not set, "application/json" is used.
ContentType string
// GroupVersion is the API version to talk to. Must be provided when initializing
// a RESTClient directly. When initializing a Client, will be set with the default
// code version. This is used as the default group version for VersionedParams.
GroupVersion schema.GroupVersion
// Negotiator is used for obtaining encoders and decoders for multiple
// supported media types.
Negotiator runtime.ClientNegotiator
}
// RESTClient imposes common Kubernetes API conventions on a set of resource paths.
// The baseURL is expected to point to an HTTP or HTTPS path that is the parent
// of one or more resources. The server should return a decodable API resource
// object, or an api.Status object which contains information about the reason for
// any failure.
//
// Most consumers should use client.New() to get a Kubernetes API client.
type RESTClient struct {
// base is the root URL for all invocations of the client
base *url.URL
// versionedAPIPath is a path segment connecting the base URL to the resource root
versionedAPIPath string
// content describes how a RESTClient encodes and decodes responses.
content requestClientContentConfigProvider
// creates BackoffManager that is passed to requests.
createBackoffMgr func() BackoffManager
// rateLimiter is shared among all requests created by this client unless specifically
// overridden.
rateLimiter flowcontrol.RateLimiter
// warningHandler is shared among all requests created by this client.
// If not set, defaultWarningHandler is used.
warningHandler WarningHandler
// Set specific behavior of the client. If not set http.DefaultClient will be used.
Client *http.Client
}
// NewRESTClient creates a new RESTClient. This client performs generic REST functions
// such as Get, Put, Post, and Delete on specified paths.
func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ClientContentConfig, rateLimiter flowcontrol.RateLimiter, client *http.Client) (*RESTClient, error) {
base := *baseURL
if !strings.HasSuffix(base.Path, "/") {
base.Path += "/"
}
base.RawQuery = ""
base.Fragment = ""
return &RESTClient{
base: &base,
versionedAPIPath: versionedAPIPath,
content: requestClientContentConfigProvider{base: scrubCBORContentConfigIfDisabled(config)},
createBackoffMgr: readExpBackoffConfig,
rateLimiter: rateLimiter,
Client: client,
}, nil
}
func scrubCBORContentConfigIfDisabled(content ClientContentConfig) ClientContentConfig {
if clientfeatures.FeatureGates().Enabled(clientfeatures.ClientsAllowCBOR) {
content.Negotiator = clientNegotiatorWithCBORSequenceStreamDecoder{content.Negotiator}
return content
}
if mediatype, _, err := mime.ParseMediaType(content.ContentType); err == nil && mediatype == "application/cbor" {
content.ContentType = "application/json"
}
clauses := goautoneg.ParseAccept(content.AcceptContentTypes)
scrubbed := false
for i, clause := range clauses {
if clause.Type == "application" && clause.SubType == "cbor" {
scrubbed = true
clauses[i].SubType = "json"
}
}
if !scrubbed {
// No application/cbor in AcceptContentTypes, nothing more to do.
return content
}
parts := make([]string, 0, len(clauses))
for _, clause := range clauses {
// ParseAccept does not store the parameter "q" in Params.
params := clause.Params
if clause.Q < 1 { // omit q=1, it's the default
if params == nil {
params = make(map[string]string, 1)
}
params["q"] = strconv.FormatFloat(clause.Q, 'g', 3, 32)
}
parts = append(parts, mime.FormatMediaType(fmt.Sprintf("%s/%s", clause.Type, clause.SubType), params))
}
content.AcceptContentTypes = strings.Join(parts, ",")
return content
}
// GetRateLimiter returns rate limiter for a given client, or nil if it's called on a nil client
func (c *RESTClient) GetRateLimiter() flowcontrol.RateLimiter {
if c == nil {
return nil
}
return c.rateLimiter
}
// readExpBackoffConfig handles the internal logic of determining what the
// backoff policy is. By default if no information is available, NoBackoff.
// TODO Generalize this see #17727 .
func readExpBackoffConfig() BackoffManager {
backoffBase := os.Getenv(envBackoffBase)
backoffDuration := os.Getenv(envBackoffDuration)
backoffBaseInt, errBase := strconv.ParseInt(backoffBase, 10, 64)
backoffDurationInt, errDuration := strconv.ParseInt(backoffDuration, 10, 64)
if errBase != nil || errDuration != nil {
return &NoBackoff{}
}
return &URLBackoff{
Backoff: flowcontrol.NewBackOff(
time.Duration(backoffBaseInt)*time.Second,
time.Duration(backoffDurationInt)*time.Second)}
}
// Verb begins a request with a verb (GET, POST, PUT, DELETE).
//
// Example usage of RESTClient's request building interface:
// c, err := NewRESTClient(...)
// if err != nil { ... }
// resp, err := c.Verb("GET").
//
// Path("pods").
// SelectorParam("labels", "area=staging").
// Timeout(10*time.Second).
// Do()
//
// if err != nil { ... }
// list, ok := resp.(*api.PodList)
func (c *RESTClient) Verb(verb string) *Request {
return NewRequest(c).Verb(verb)
}
// Post begins a POST request. Short for c.Verb("POST").
func (c *RESTClient) Post() *Request {
return c.Verb("POST")
}
// Put begins a PUT request. Short for c.Verb("PUT").
func (c *RESTClient) Put() *Request {
return c.Verb("PUT")
}
// Patch begins a PATCH request. Short for c.Verb("Patch").
func (c *RESTClient) Patch(pt types.PatchType) *Request {
return c.Verb("PATCH").SetHeader("Content-Type", string(pt))
}
// Get begins a GET request. Short for c.Verb("GET").
func (c *RESTClient) Get() *Request {
return c.Verb("GET")
}
// Delete begins a DELETE request. Short for c.Verb("DELETE").
func (c *RESTClient) Delete() *Request {
return c.Verb("DELETE")
}
// APIVersion returns the APIVersion this RESTClient is expected to use.
func (c *RESTClient) APIVersion() schema.GroupVersion {
config, _ := c.content.GetClientContentConfig()
return config.GroupVersion
}
// requestClientContentConfigProvider observes HTTP 415 (Unsupported Media Type) responses to detect
// that the server does not understand CBOR. Once this has happened, future requests are forced to
// use JSON so they can succeed. This is convenient for client users that want to prefer CBOR, but
// also need to interoperate with older servers so requests do not permanently fail. The clients
// will not default to using CBOR until at least all supported kube-apiservers have enable-CBOR
// locked to true, so this path will be rarely taken. Additionally, all generated clients accessing
// built-in kube resources are forced to protobuf, so those will not degrade to JSON.
type requestClientContentConfigProvider struct {
base ClientContentConfig
// Becomes permanently true if a server responds with HTTP 415 (Unsupported Media Type) to a
// request with "Content-Type" header containing the CBOR media type.
sawUnsupportedMediaTypeForCBOR atomic.Bool
}
// GetClientContentConfig returns the ClientContentConfig that should be used for new requests by
// this client and true if the request ContentType was selected by default.
func (p *requestClientContentConfigProvider) GetClientContentConfig() (ClientContentConfig, bool) {
config := p.base
defaulted := config.ContentType == ""
if defaulted {
config.ContentType = "application/json"
}
if !clientfeatures.FeatureGates().Enabled(clientfeatures.ClientsAllowCBOR) {
return config, defaulted
}
if defaulted && clientfeatures.FeatureGates().Enabled(clientfeatures.ClientsPreferCBOR) {
config.ContentType = "application/cbor"
}
if sawUnsupportedMediaTypeForCBOR := p.sawUnsupportedMediaTypeForCBOR.Load(); !sawUnsupportedMediaTypeForCBOR {
return config, defaulted
}
if mediaType, _, _ := mime.ParseMediaType(config.ContentType); mediaType != runtime.ContentTypeCBOR {
return config, defaulted
}
// The effective ContentType is CBOR and the client has previously received an HTTP 415 in
// response to a CBOR request. Override ContentType to JSON.
config.ContentType = runtime.ContentTypeJSON
return config, defaulted
}
// UnsupportedMediaType reports that the server has responded to a request with HTTP 415 Unsupported
// Media Type.
func (p *requestClientContentConfigProvider) UnsupportedMediaType(requestContentType string) {
if !clientfeatures.FeatureGates().Enabled(clientfeatures.ClientsAllowCBOR) {
return
}
// This could be extended to consider the Content-Encoding request header, the Accept and
// Accept-Encoding response headers, the request method, and URI (as mentioned in
// https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.16). The request Content-Type
// header is sufficient to implement a blanket CBOR fallback mechanism.
requestContentType, _, _ = mime.ParseMediaType(requestContentType)
switch requestContentType {
case runtime.ContentTypeCBOR, string(types.ApplyCBORPatchType):
p.sawUnsupportedMediaTypeForCBOR.Store(true)
}
}
// clientNegotiatorWithCBORSequenceStreamDecoder is a ClientNegotiator that delegates to another
// ClientNegotiator to select the appropriate Encoder or Decoder for a given media type. As a
// special case, it will resolve "application/cbor-seq" (a CBOR Sequence, the concatenation of zero
// or more CBOR data items) as an alias for "application/cbor" (exactly one CBOR data item) when
// selecting a stream decoder.
type clientNegotiatorWithCBORSequenceStreamDecoder struct {
negotiator runtime.ClientNegotiator
}
func (n clientNegotiatorWithCBORSequenceStreamDecoder) Encoder(contentType string, params map[string]string) (runtime.Encoder, error) {
return n.negotiator.Encoder(contentType, params)
}
func (n clientNegotiatorWithCBORSequenceStreamDecoder) Decoder(contentType string, params map[string]string) (runtime.Decoder, error) {
return n.negotiator.Decoder(contentType, params)
}
func (n clientNegotiatorWithCBORSequenceStreamDecoder) StreamDecoder(contentType string, params map[string]string) (runtime.Decoder, runtime.Serializer, runtime.Framer, error) {
if !clientfeatures.FeatureGates().Enabled(clientfeatures.ClientsAllowCBOR) {
return n.negotiator.StreamDecoder(contentType, params)
}
switch contentType {
case runtime.ContentTypeCBORSequence:
return n.negotiator.StreamDecoder(runtime.ContentTypeCBOR, params)
case runtime.ContentTypeCBOR:
// This media type is only appropriate for exactly one data item, not the zero or
// more events of a watch stream.
return nil, nil, nil, runtime.NegotiateError{ContentType: contentType, Stream: true}
default:
return n.negotiator.StreamDecoder(contentType, params)
}
}

693
e2e/vendor/k8s.io/client-go/rest/config.go generated vendored Normal file
View File

@ -0,0 +1,693 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
gruntime "runtime"
"strings"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer/cbor"
"k8s.io/client-go/features"
"k8s.io/client-go/pkg/version"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/transport"
certutil "k8s.io/client-go/util/cert"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog/v2"
)
const (
DefaultQPS float32 = 5.0
DefaultBurst int = 10
)
var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
// Config holds the common attributes that can be passed to a Kubernetes client on
// initialization.
type Config struct {
// Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
// If a URL is given then the (optional) Path of that URL represents a prefix that must
// be appended to all request URIs used to access the apiserver. This allows a frontend
// proxy to easily relocate all of the apiserver endpoints.
Host string
// APIPath is a sub-path that points to an API root.
APIPath string
// ContentConfig contains settings that affect how objects are transformed when
// sent to the server.
ContentConfig
// Server requires Basic authentication
Username string
Password string `datapolicy:"password"`
// Server requires Bearer authentication. This client will not attempt to use
// refresh tokens for an OAuth2 flow.
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string `datapolicy:"token"`
// Path to a file containing a BearerToken.
// If set, the contents are periodically read.
// The last successfully read value takes precedence over BearerToken.
BearerTokenFile string
// Impersonate is the configuration that RESTClient will use for impersonation.
Impersonate ImpersonationConfig
// Server requires plugin-specified authentication.
AuthProvider *clientcmdapi.AuthProviderConfig
// Callback to persist config for AuthProvider.
AuthConfigPersister AuthProviderConfigPersister
// Exec-based authentication provider.
ExecProvider *clientcmdapi.ExecConfig
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
// UserAgent is an optional field that specifies the caller of this request.
UserAgent string
// DisableCompression bypasses automatic GZip compression requests to the
// server.
DisableCompression bool
// Transport may be used for custom HTTP behavior. This attribute may not
// be specified with the TLS client certificate options. Use WrapTransport
// to provide additional per-server middleware behavior.
Transport http.RoundTripper
// WrapTransport will be invoked for custom HTTP behavior after the underlying
// transport is initialized (either the transport created from TLSClientConfig,
// Transport, or http.DefaultTransport). The config may layer other RoundTrippers
// on top of the returned RoundTripper.
//
// A future release will change this field to an array. Use config.Wrap()
// instead of setting this value directly.
WrapTransport transport.WrapperFunc
// QPS indicates the maximum QPS to the master from this client.
// If it's zero, the created RESTClient will use DefaultQPS: 5
//
// Setting this to a negative value will disable client-side ratelimiting
// unless `Ratelimiter` is also set.
QPS float32
// Maximum burst for throttle.
// If it's zero, the created RESTClient will use DefaultBurst: 10.
Burst int
// Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
RateLimiter flowcontrol.RateLimiter
// WarningHandler handles warnings in server responses.
// If not set, the default warning handler is used.
// See documentation for SetDefaultWarningHandler() for details.
WarningHandler WarningHandler
// The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
Timeout time.Duration
// Dial specifies the dial function for creating unencrypted TCP connections.
Dial func(ctx context.Context, network, address string) (net.Conn, error)
// Proxy is the proxy func to be used for all requests made by this
// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
// returns a nil *URL, no proxy is used.
//
// socks5 proxying does not currently support spdy streaming endpoints.
Proxy func(*http.Request) (*url.URL, error)
// Version forces a specific version to be used (if registered)
// Do we need this?
// Version string
}
var _ fmt.Stringer = new(Config)
var _ fmt.GoStringer = new(Config)
type sanitizedConfig *Config
type sanitizedAuthConfigPersister struct{ AuthProviderConfigPersister }
func (sanitizedAuthConfigPersister) GoString() string {
return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
}
func (sanitizedAuthConfigPersister) String() string {
return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
}
type sanitizedObject struct{ runtime.Object }
func (sanitizedObject) GoString() string {
return "runtime.Object(--- REDACTED ---)"
}
func (sanitizedObject) String() string {
return "runtime.Object(--- REDACTED ---)"
}
// GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
// to prevent accidental leaking via logs.
func (c *Config) GoString() string {
return c.String()
}
// String implements fmt.Stringer and sanitizes sensitive fields of Config to
// prevent accidental leaking via logs.
func (c *Config) String() string {
if c == nil {
return "<nil>"
}
cc := sanitizedConfig(CopyConfig(c))
// Explicitly mark non-empty credential fields as redacted.
if cc.Password != "" {
cc.Password = "--- REDACTED ---"
}
if cc.BearerToken != "" {
cc.BearerToken = "--- REDACTED ---"
}
if cc.AuthConfigPersister != nil {
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
}
if cc.ExecProvider != nil && cc.ExecProvider.Config != nil {
cc.ExecProvider.Config = sanitizedObject{Object: cc.ExecProvider.Config}
}
return fmt.Sprintf("%#v", cc)
}
// ImpersonationConfig has all the available impersonation options
type ImpersonationConfig struct {
// UserName is the username to impersonate on each request.
UserName string
// UID is a unique value that identifies the user.
UID string
// Groups are the groups to impersonate on each request.
Groups []string
// Extra is a free-form field which can be used to link some authentication information
// to authorization information. This field allows you to impersonate it.
Extra map[string][]string
}
// +k8s:deepcopy-gen=true
// TLSClientConfig contains settings to enable transport layer security
type TLSClientConfig struct {
// Server should be accessed without verifying the TLS certificate. For testing only.
Insecure bool
// ServerName is passed to the server for SNI and is used in the client to check server
// certificates against. If ServerName is empty, the hostname used to contact the
// server is used.
ServerName string
// Server requires TLS client certificate authentication
CertFile string
// Server requires TLS client certificate authentication
KeyFile string
// Trusted root certificates for server
CAFile string
// CertData holds PEM-encoded bytes (typically read from a client certificate file).
// CertData takes precedence over CertFile
CertData []byte
// KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
// KeyData takes precedence over KeyFile
KeyData []byte `datapolicy:"security-key"`
// CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
// CAData takes precedence over CAFile
CAData []byte
// NextProtos is a list of supported application level protocols, in order of preference.
// Used to populate tls.Config.NextProtos.
// To indicate to the server http/1.1 is preferred over http/2, set to ["http/1.1", "h2"] (though the server is free to ignore that preference).
// To use only http/1.1, set to ["http/1.1"].
NextProtos []string
}
var _ fmt.Stringer = TLSClientConfig{}
var _ fmt.GoStringer = TLSClientConfig{}
type sanitizedTLSClientConfig TLSClientConfig
// GoString implements fmt.GoStringer and sanitizes sensitive fields of
// TLSClientConfig to prevent accidental leaking via logs.
func (c TLSClientConfig) GoString() string {
return c.String()
}
// String implements fmt.Stringer and sanitizes sensitive fields of
// TLSClientConfig to prevent accidental leaking via logs.
func (c TLSClientConfig) String() string {
cc := sanitizedTLSClientConfig{
Insecure: c.Insecure,
ServerName: c.ServerName,
CertFile: c.CertFile,
KeyFile: c.KeyFile,
CAFile: c.CAFile,
CertData: c.CertData,
KeyData: c.KeyData,
CAData: c.CAData,
NextProtos: c.NextProtos,
}
// Explicitly mark non-empty credential fields as redacted.
if len(cc.CertData) != 0 {
cc.CertData = []byte("--- TRUNCATED ---")
}
if len(cc.KeyData) != 0 {
cc.KeyData = []byte("--- REDACTED ---")
}
return fmt.Sprintf("%#v", cc)
}
type ContentConfig struct {
// AcceptContentTypes specifies the types the client will accept and is optional.
// If not set, ContentType will be used to define the Accept header
AcceptContentTypes string
// ContentType specifies the wire format used to communicate with the server.
// This value will be set as the Accept header on requests made to the server, and
// as the default content type on any object sent to the server. If not set,
// "application/json" is used.
ContentType string
// GroupVersion is the API version to talk to. Must be provided when initializing
// a RESTClient directly. When initializing a Client, will be set with the default
// code version.
GroupVersion *schema.GroupVersion
// NegotiatedSerializer is used for obtaining encoders and decoders for multiple
// supported media types.
//
// TODO: NegotiatedSerializer will be phased out as internal clients are removed
// from Kubernetes.
NegotiatedSerializer runtime.NegotiatedSerializer
}
// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
// object. Note that a RESTClient may require fields that are optional when initializing a Client.
// A RESTClient created by this method is generic - it expects to operate on an API that follows
// the Kubernetes conventions, but may not be the Kubernetes API.
// RESTClientFor is equivalent to calling RESTClientForConfigAndClient(config, httpClient),
// where httpClient was generated with HTTPClientFor(config).
func RESTClientFor(config *Config) (*RESTClient, error) {
if config.GroupVersion == nil {
return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
}
if config.NegotiatedSerializer == nil {
return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
}
// Validate config.Host before constructing the transport/client so we can fail fast.
// ServerURL will be obtained later in RESTClientForConfigAndClient()
_, _, err := DefaultServerUrlFor(config)
if err != nil {
return nil, err
}
httpClient, err := HTTPClientFor(config)
if err != nil {
return nil, err
}
return RESTClientForConfigAndClient(config, httpClient)
}
// RESTClientForConfigAndClient returns a RESTClient that satisfies the requested attributes on a
// client Config object.
// Unlike RESTClientFor, RESTClientForConfigAndClient allows to pass an http.Client that is shared
// between all the API Groups and Versions.
// Note that the http client takes precedence over the transport values configured.
// The http client defaults to the `http.DefaultClient` if nil.
func RESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
if config.GroupVersion == nil {
return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
}
if config.NegotiatedSerializer == nil {
return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
}
baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
if err != nil {
return nil, err
}
rateLimiter := config.RateLimiter
if rateLimiter == nil {
qps := config.QPS
if config.QPS == 0.0 {
qps = DefaultQPS
}
burst := config.Burst
if config.Burst == 0 {
burst = DefaultBurst
}
if qps > 0 {
rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
}
}
var gv schema.GroupVersion
if config.GroupVersion != nil {
gv = *config.GroupVersion
}
clientContent := ClientContentConfig{
AcceptContentTypes: config.AcceptContentTypes,
ContentType: config.ContentType,
GroupVersion: gv,
Negotiator: runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
}
restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
if err == nil && config.WarningHandler != nil {
restClient.warningHandler = config.WarningHandler
}
return restClient, err
}
// UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
// the config.Version to be empty.
func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
if config.NegotiatedSerializer == nil {
return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
}
// Validate config.Host before constructing the transport/client so we can fail fast.
// ServerURL will be obtained later in UnversionedRESTClientForConfigAndClient()
_, _, err := DefaultServerUrlFor(config)
if err != nil {
return nil, err
}
httpClient, err := HTTPClientFor(config)
if err != nil {
return nil, err
}
return UnversionedRESTClientForConfigAndClient(config, httpClient)
}
// UnversionedRESTClientForConfigAndClient is the same as RESTClientForConfigAndClient,
// except that it allows the config.Version to be empty.
func UnversionedRESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
if config.NegotiatedSerializer == nil {
return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
}
baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
if err != nil {
return nil, err
}
rateLimiter := config.RateLimiter
if rateLimiter == nil {
qps := config.QPS
if config.QPS == 0.0 {
qps = DefaultQPS
}
burst := config.Burst
if config.Burst == 0 {
burst = DefaultBurst
}
if qps > 0 {
rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
}
}
gv := metav1.SchemeGroupVersion
if config.GroupVersion != nil {
gv = *config.GroupVersion
}
clientContent := ClientContentConfig{
AcceptContentTypes: config.AcceptContentTypes,
ContentType: config.ContentType,
GroupVersion: gv,
Negotiator: runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
}
restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
if err == nil && config.WarningHandler != nil {
restClient.warningHandler = config.WarningHandler
}
return restClient, err
}
// SetKubernetesDefaults sets default values on the provided client config for accessing the
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
func SetKubernetesDefaults(config *Config) error {
if len(config.UserAgent) == 0 {
config.UserAgent = DefaultKubernetesUserAgent()
}
return nil
}
// adjustCommit returns sufficient significant figures of the commit's git hash.
func adjustCommit(c string) string {
if len(c) == 0 {
return "unknown"
}
if len(c) > 7 {
return c[:7]
}
return c
}
// adjustVersion strips "alpha", "beta", etc. from version in form
// major.minor.patch-[alpha|beta|etc].
func adjustVersion(v string) string {
if len(v) == 0 {
return "unknown"
}
seg := strings.SplitN(v, "-", 2)
return seg[0]
}
// adjustCommand returns the last component of the
// OS-specific command path for use in User-Agent.
func adjustCommand(p string) string {
// Unlikely, but better than returning "".
if len(p) == 0 {
return "unknown"
}
return filepath.Base(p)
}
// buildUserAgent builds a User-Agent string from given args.
func buildUserAgent(command, version, os, arch, commit string) string {
return fmt.Sprintf(
"%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
}
// DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
func DefaultKubernetesUserAgent() string {
return buildUserAgent(
adjustCommand(os.Args[0]),
adjustVersion(version.Get().GitVersion),
gruntime.GOOS,
gruntime.GOARCH,
adjustCommit(version.Get().GitCommit))
}
// InClusterConfig returns a config object which uses the service account
// kubernetes gives to pods. It's intended for clients that expect to be
// running inside a pod running on kubernetes. It will return ErrNotInCluster
// if called from a process not running in a kubernetes environment.
func InClusterConfig() (*Config, error) {
const (
tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
if len(host) == 0 || len(port) == 0 {
return nil, ErrNotInCluster
}
token, err := os.ReadFile(tokenFile)
if err != nil {
return nil, err
}
tlsClientConfig := TLSClientConfig{}
if _, err := certutil.NewPool(rootCAFile); err != nil {
klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
} else {
tlsClientConfig.CAFile = rootCAFile
}
return &Config{
// TODO: switch to using cluster DNS.
Host: "https://" + net.JoinHostPort(host, port),
TLSClientConfig: tlsClientConfig,
BearerToken: string(token),
BearerTokenFile: tokenFile,
}, nil
}
// IsConfigTransportTLS returns true if and only if the provided
// config will result in a protected connection to the server when it
// is passed to restclient.RESTClientFor(). Use to determine when to
// send credentials over the wire.
//
// Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
// still possible.
func IsConfigTransportTLS(config Config) bool {
baseURL, _, err := DefaultServerUrlFor(&config)
if err != nil {
return false
}
return baseURL.Scheme == "https"
}
// LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
// either populated or were empty to start.
func LoadTLSFiles(c *Config) error {
var err error
c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
if err != nil {
return err
}
c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
if err != nil {
return err
}
c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
return err
}
// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
// or an error if an error occurred reading the file
func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
if len(data) > 0 {
return data, nil
}
if len(file) > 0 {
fileData, err := os.ReadFile(file)
if err != nil {
return []byte{}, err
}
return fileData, nil
}
return nil, nil
}
func AddUserAgent(config *Config, userAgent string) *Config {
fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
config.UserAgent = fullUserAgent
return config
}
// AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) and custom transports (WrapTransport, Transport) removed
func AnonymousClientConfig(config *Config) *Config {
// copy only known safe fields
return &Config{
Host: config.Host,
APIPath: config.APIPath,
ContentConfig: config.ContentConfig,
TLSClientConfig: TLSClientConfig{
Insecure: config.Insecure,
ServerName: config.ServerName,
CAFile: config.TLSClientConfig.CAFile,
CAData: config.TLSClientConfig.CAData,
NextProtos: config.TLSClientConfig.NextProtos,
},
RateLimiter: config.RateLimiter,
WarningHandler: config.WarningHandler,
UserAgent: config.UserAgent,
DisableCompression: config.DisableCompression,
QPS: config.QPS,
Burst: config.Burst,
Timeout: config.Timeout,
Dial: config.Dial,
Proxy: config.Proxy,
}
}
// CopyConfig returns a copy of the given config
func CopyConfig(config *Config) *Config {
c := &Config{
Host: config.Host,
APIPath: config.APIPath,
ContentConfig: config.ContentConfig,
Username: config.Username,
Password: config.Password,
BearerToken: config.BearerToken,
BearerTokenFile: config.BearerTokenFile,
Impersonate: ImpersonationConfig{
UserName: config.Impersonate.UserName,
UID: config.Impersonate.UID,
Groups: config.Impersonate.Groups,
Extra: config.Impersonate.Extra,
},
AuthProvider: config.AuthProvider,
AuthConfigPersister: config.AuthConfigPersister,
ExecProvider: config.ExecProvider,
TLSClientConfig: TLSClientConfig{
Insecure: config.TLSClientConfig.Insecure,
ServerName: config.TLSClientConfig.ServerName,
CertFile: config.TLSClientConfig.CertFile,
KeyFile: config.TLSClientConfig.KeyFile,
CAFile: config.TLSClientConfig.CAFile,
CertData: config.TLSClientConfig.CertData,
KeyData: config.TLSClientConfig.KeyData,
CAData: config.TLSClientConfig.CAData,
NextProtos: config.TLSClientConfig.NextProtos,
},
UserAgent: config.UserAgent,
DisableCompression: config.DisableCompression,
Transport: config.Transport,
WrapTransport: config.WrapTransport,
QPS: config.QPS,
Burst: config.Burst,
RateLimiter: config.RateLimiter,
WarningHandler: config.WarningHandler,
Timeout: config.Timeout,
Dial: config.Dial,
Proxy: config.Proxy,
}
if config.ExecProvider != nil && config.ExecProvider.Config != nil {
c.ExecProvider.Config = config.ExecProvider.Config.DeepCopyObject()
}
return c
}
// CodecFactoryForGeneratedClient returns the provided CodecFactory if there are no enabled client
// feature gates affecting serialization. Otherwise, it constructs and returns a new CodecFactory
// from the provided Scheme.
//
// This is supported ONLY for use by clients generated with client-gen. The caller is responsible
// for ensuring that the CodecFactory argument was constructed using the Scheme argument.
func CodecFactoryForGeneratedClient(scheme *runtime.Scheme, codecs serializer.CodecFactory) serializer.CodecFactory {
if !features.FeatureGates().Enabled(features.ClientsAllowCBOR) {
// NOTE: This assumes client-gen will not generate CBOR-enabled Codecs as long as
// the feature gate exists.
return codecs
}
return serializer.NewCodecFactory(scheme, serializer.WithSerializer(cbor.NewSerializerInfo))
}

86
e2e/vendor/k8s.io/client-go/rest/exec.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"net/http"
"net/url"
clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
)
// This file contains Config logic related to exec credential plugins.
// ConfigToExecCluster creates a clientauthenticationapi.Cluster with the corresponding fields from
// the provided Config.
func ConfigToExecCluster(config *Config) (*clientauthenticationapi.Cluster, error) {
caData, err := dataFromSliceOrFile(config.CAData, config.CAFile)
if err != nil {
return nil, fmt.Errorf("failed to load CA bundle for execProvider: %v", err)
}
var proxyURL string
if config.Proxy != nil {
req, err := http.NewRequest("", config.Host, nil)
if err != nil {
return nil, fmt.Errorf("failed to create proxy URL request for execProvider: %w", err)
}
url, err := config.Proxy(req)
if err != nil {
return nil, fmt.Errorf("failed to get proxy URL for execProvider: %w", err)
}
if url != nil {
proxyURL = url.String()
}
}
return &clientauthenticationapi.Cluster{
Server: config.Host,
TLSServerName: config.ServerName,
InsecureSkipTLSVerify: config.Insecure,
CertificateAuthorityData: caData,
ProxyURL: proxyURL,
DisableCompression: config.DisableCompression,
Config: config.ExecProvider.Config,
}, nil
}
// ExecClusterToConfig creates a Config with the corresponding fields from the provided
// clientauthenticationapi.Cluster. The returned Config will be anonymous (i.e., it will not have
// any authentication-related fields set).
func ExecClusterToConfig(cluster *clientauthenticationapi.Cluster) (*Config, error) {
var proxy func(*http.Request) (*url.URL, error)
if cluster.ProxyURL != "" {
proxyURL, err := url.Parse(cluster.ProxyURL)
if err != nil {
return nil, fmt.Errorf("cannot parse proxy URL: %w", err)
}
proxy = http.ProxyURL(proxyURL)
}
return &Config{
Host: cluster.Server,
TLSClientConfig: TLSClientConfig{
Insecure: cluster.InsecureSkipTLSVerify,
ServerName: cluster.TLSServerName,
CAData: cluster.CertificateAuthorityData,
},
Proxy: proxy,
DisableCompression: cluster.DisableCompression,
}, nil
}

118
e2e/vendor/k8s.io/client-go/rest/fake/fake.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This is made a separate package and should only be imported by tests, because
// it imports testapi
package fake
import (
"net/http"
"net/url"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/util/flowcontrol"
)
// CreateHTTPClient creates an http.Client that will invoke the provided roundTripper func
// when a request is made.
func CreateHTTPClient(roundTripper func(*http.Request) (*http.Response, error)) *http.Client {
return &http.Client{
Transport: roundTripperFunc(roundTripper),
}
}
type roundTripperFunc func(*http.Request) (*http.Response, error)
func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
// RESTClient provides a fake RESTClient interface. It is used to mock network
// interactions via a rest.Request, or to make them via the provided Client to
// a specific server.
type RESTClient struct {
NegotiatedSerializer runtime.NegotiatedSerializer
GroupVersion schema.GroupVersion
VersionedAPIPath string
// Err is returned when any request would be made to the server. If Err is set,
// Req will not be recorded, Resp will not be returned, and Client will not be
// invoked.
Err error
// Req is set to the last request that was executed (had the methods Do/DoRaw) invoked.
Req *http.Request
// If Client is specified, the client will be invoked instead of returning Resp if
// Err is not set.
Client *http.Client
// Resp is returned to the caller after Req is recorded, unless Err or Client are set.
Resp *http.Response
}
func (c *RESTClient) Get() *restclient.Request {
return c.Verb("GET")
}
func (c *RESTClient) Put() *restclient.Request {
return c.Verb("PUT")
}
func (c *RESTClient) Patch(pt types.PatchType) *restclient.Request {
return c.Verb("PATCH").SetHeader("Content-Type", string(pt))
}
func (c *RESTClient) Post() *restclient.Request {
return c.Verb("POST")
}
func (c *RESTClient) Delete() *restclient.Request {
return c.Verb("DELETE")
}
func (c *RESTClient) Verb(verb string) *restclient.Request {
return c.Request().Verb(verb)
}
func (c *RESTClient) APIVersion() schema.GroupVersion {
return c.GroupVersion
}
func (c *RESTClient) GetRateLimiter() flowcontrol.RateLimiter {
return nil
}
func (c *RESTClient) Request() *restclient.Request {
config := restclient.ClientContentConfig{
ContentType: runtime.ContentTypeJSON,
GroupVersion: c.GroupVersion,
Negotiator: runtime.NewClientNegotiator(c.NegotiatedSerializer, c.GroupVersion),
}
return restclient.NewRequestWithClient(&url.URL{Scheme: "https", Host: "localhost"}, c.VersionedAPIPath, config, CreateHTTPClient(c.do))
}
// do is invoked when a Request() created by this client is executed.
func (c *RESTClient) do(req *http.Request) (*http.Response, error) {
if c.Err != nil {
return nil, c.Err
}
c.Req = req
if c.Client != nil {
return c.Client.Do(req)
}
return c.Resp, nil
}

84
e2e/vendor/k8s.io/client-go/rest/plugin.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"net/http"
"sync"
"k8s.io/klog/v2"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
type AuthProvider interface {
// WrapTransport allows the plugin to create a modified RoundTripper that
// attaches authorization headers (or other info) to requests.
WrapTransport(http.RoundTripper) http.RoundTripper
// Login allows the plugin to initialize its configuration. It must not
// require direct user interaction.
Login() error
}
// Factory generates an AuthProvider plugin.
//
// clusterAddress is the address of the current cluster.
// config is the initial configuration for this plugin.
// persister allows the plugin to save updated configuration.
type Factory func(clusterAddress string, config map[string]string, persister AuthProviderConfigPersister) (AuthProvider, error)
// AuthProviderConfigPersister allows a plugin to persist configuration info
// for just itself.
type AuthProviderConfigPersister interface {
Persist(map[string]string) error
}
type noopPersister struct{}
func (n *noopPersister) Persist(_ map[string]string) error {
// no operation persister
return nil
}
// All registered auth provider plugins.
var pluginsLock sync.Mutex
var plugins = make(map[string]Factory)
func RegisterAuthProviderPlugin(name string, plugin Factory) error {
pluginsLock.Lock()
defer pluginsLock.Unlock()
if _, found := plugins[name]; found {
return fmt.Errorf("auth Provider Plugin %q was registered twice", name)
}
klog.V(4).Infof("Registered Auth Provider Plugin %q", name)
plugins[name] = plugin
return nil
}
func GetAuthProvider(clusterAddress string, apc *clientcmdapi.AuthProviderConfig, persister AuthProviderConfigPersister) (AuthProvider, error) {
pluginsLock.Lock()
defer pluginsLock.Unlock()
p, ok := plugins[apc.Name]
if !ok {
return nil, fmt.Errorf("no Auth Provider found for name %q", apc.Name)
}
if persister == nil {
persister = &noopPersister{}
}
return p(clusterAddress, apc.Config, persister)
}

1700
e2e/vendor/k8s.io/client-go/rest/request.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

155
e2e/vendor/k8s.io/client-go/rest/transport.go generated vendored Normal file
View File

@ -0,0 +1,155 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"crypto/tls"
"errors"
"net/http"
"k8s.io/client-go/pkg/apis/clientauthentication"
"k8s.io/client-go/plugin/pkg/client/auth/exec"
"k8s.io/client-go/transport"
)
// HTTPClientFor returns an http.Client that will provide the authentication
// or transport level security defined by the provided Config. Will return the
// default http.DefaultClient if no special case behavior is needed.
func HTTPClientFor(config *Config) (*http.Client, error) {
transport, err := TransportFor(config)
if err != nil {
return nil, err
}
var httpClient *http.Client
if transport != http.DefaultTransport || config.Timeout > 0 {
httpClient = &http.Client{
Transport: transport,
Timeout: config.Timeout,
}
} else {
httpClient = http.DefaultClient
}
return httpClient, nil
}
// TLSConfigFor returns a tls.Config that will provide the transport level security defined
// by the provided Config. Will return nil if no transport level security is requested.
func TLSConfigFor(config *Config) (*tls.Config, error) {
cfg, err := config.TransportConfig()
if err != nil {
return nil, err
}
return transport.TLSConfigFor(cfg)
}
// TransportFor returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config. Will return the
// default http.DefaultTransport if no special case behavior is needed.
func TransportFor(config *Config) (http.RoundTripper, error) {
cfg, err := config.TransportConfig()
if err != nil {
return nil, err
}
return transport.New(cfg)
}
// HTTPWrappersForConfig wraps a round tripper with any relevant layered behavior from the
// config. Exposed to allow more clients that need HTTP-like behavior but then must hijack
// the underlying connection (like WebSocket or HTTP2 clients). Pure HTTP clients should use
// the higher level TransportFor or RESTClientFor methods.
func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
cfg, err := config.TransportConfig()
if err != nil {
return nil, err
}
return transport.HTTPWrappersForConfig(cfg, rt)
}
// TransportConfig converts a client config to an appropriate transport config.
func (c *Config) TransportConfig() (*transport.Config, error) {
conf := &transport.Config{
UserAgent: c.UserAgent,
Transport: c.Transport,
WrapTransport: c.WrapTransport,
DisableCompression: c.DisableCompression,
TLS: transport.TLSConfig{
Insecure: c.Insecure,
ServerName: c.ServerName,
CAFile: c.CAFile,
CAData: c.CAData,
CertFile: c.CertFile,
CertData: c.CertData,
KeyFile: c.KeyFile,
KeyData: c.KeyData,
NextProtos: c.NextProtos,
},
Username: c.Username,
Password: c.Password,
BearerToken: c.BearerToken,
BearerTokenFile: c.BearerTokenFile,
Impersonate: transport.ImpersonationConfig{
UserName: c.Impersonate.UserName,
UID: c.Impersonate.UID,
Groups: c.Impersonate.Groups,
Extra: c.Impersonate.Extra,
},
Proxy: c.Proxy,
}
if c.Dial != nil {
conf.DialHolder = &transport.DialHolder{Dial: c.Dial}
}
if c.ExecProvider != nil && c.AuthProvider != nil {
return nil, errors.New("execProvider and authProvider cannot be used in combination")
}
if c.ExecProvider != nil {
var cluster *clientauthentication.Cluster
if c.ExecProvider.ProvideClusterInfo {
var err error
cluster, err = ConfigToExecCluster(c)
if err != nil {
return nil, err
}
}
provider, err := exec.GetAuthenticator(c.ExecProvider, cluster)
if err != nil {
return nil, err
}
if err := provider.UpdateTransportConfig(conf); err != nil {
return nil, err
}
}
if c.AuthProvider != nil {
provider, err := GetAuthProvider(c.Host, c.AuthProvider, c.AuthConfigPersister)
if err != nil {
return nil, err
}
conf.Wrap(provider.WrapTransport)
}
return conf, nil
}
// Wrap adds a transport middleware function that will give the caller
// an opportunity to wrap the underlying http.RoundTripper prior to the
// first API call being made. The provided function is invoked after any
// existing transport wrappers are invoked.
func (c *Config) Wrap(fn transport.WrapperFunc) {
c.WrapTransport = transport.Wrappers(c.WrapTransport, fn)
}

97
e2e/vendor/k8s.io/client-go/rest/url_utils.go generated vendored Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"net/url"
"path"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// DefaultServerURL converts a host, host:port, or URL string to the default base server API path
// to use with a Client at a given API version following the standard conventions for a
// Kubernetes API.
func DefaultServerURL(host, apiPath string, groupVersion schema.GroupVersion, defaultTLS bool) (*url.URL, string, error) {
if host == "" {
return nil, "", fmt.Errorf("host must be a URL or a host:port pair")
}
base := host
hostURL, err := url.Parse(base)
if err != nil || hostURL.Scheme == "" || hostURL.Host == "" {
scheme := "http://"
if defaultTLS {
scheme = "https://"
}
hostURL, err = url.Parse(scheme + base)
if err != nil {
return nil, "", err
}
if hostURL.Path != "" && hostURL.Path != "/" {
return nil, "", fmt.Errorf("host must be a URL or a host:port pair: %q", base)
}
}
// hostURL.Path is optional; a non-empty Path is treated as a prefix that is to be applied to
// all URIs used to access the host. this is useful when there's a proxy in front of the
// apiserver that has relocated the apiserver endpoints, forwarding all requests from, for
// example, /a/b/c to the apiserver. in this case the Path should be /a/b/c.
//
// if running without a frontend proxy (that changes the location of the apiserver), then
// hostURL.Path should be blank.
//
// versionedAPIPath, a path relative to baseURL.Path, points to a versioned API base
versionedAPIPath := DefaultVersionedAPIPath(apiPath, groupVersion)
return hostURL, versionedAPIPath, nil
}
// DefaultVersionedAPIPath constructs the default path for the given group version, assuming the given
// API path, following the standard conventions of the Kubernetes API.
func DefaultVersionedAPIPath(apiPath string, groupVersion schema.GroupVersion) string {
versionedAPIPath := path.Join("/", apiPath)
// Add the version to the end of the path
if len(groupVersion.Group) > 0 {
versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Group, groupVersion.Version)
} else {
versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Version)
}
return versionedAPIPath
}
// DefaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
// requires Host and Version to be set prior to being called.
func DefaultServerUrlFor(config *Config) (*url.URL, string, error) {
// TODO: move the default to secure when the apiserver supports TLS by default
// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0
defaultTLS := hasCA || hasCert || config.Insecure
host := config.Host
if host == "" {
host = "localhost"
}
if config.GroupVersion != nil {
return DefaultServerURL(host, config.APIPath, *config.GroupVersion, defaultTLS)
}
return DefaultServerURL(host, config.APIPath, schema.GroupVersion{}, defaultTLS)
}

107
e2e/vendor/k8s.io/client-go/rest/urlbackoff.go generated vendored Normal file
View File

@ -0,0 +1,107 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"net/url"
"time"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog/v2"
)
// Set of resp. Codes that we backoff for.
// In general these should be errors that indicate a server is overloaded.
// These shouldn't be configured by any user, we set them based on conventions
// described in
var serverIsOverloadedSet = sets.NewInt(429)
var maxResponseCode = 499
type BackoffManager interface {
UpdateBackoff(actualUrl *url.URL, err error, responseCode int)
CalculateBackoff(actualUrl *url.URL) time.Duration
Sleep(d time.Duration)
}
// URLBackoff struct implements the semantics on top of Backoff which
// we need for URL specific exponential backoff.
type URLBackoff struct {
// Uses backoff as underlying implementation.
Backoff *flowcontrol.Backoff
}
// NoBackoff is a stub implementation, can be used for mocking or else as a default.
type NoBackoff struct {
}
func (n *NoBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) {
// do nothing.
}
func (n *NoBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration {
return 0 * time.Second
}
func (n *NoBackoff) Sleep(d time.Duration) {
time.Sleep(d)
}
// Disable makes the backoff trivial, i.e., sets it to zero. This might be used
// by tests which want to run 1000s of mock requests without slowing down.
func (b *URLBackoff) Disable() {
klog.V(4).Infof("Disabling backoff strategy")
b.Backoff = flowcontrol.NewBackOff(0*time.Second, 0*time.Second)
}
// baseUrlKey returns the key which urls will be mapped to.
// For example, 127.0.0.1:8080/api/v2/abcde -> 127.0.0.1:8080.
func (b *URLBackoff) baseUrlKey(rawurl *url.URL) string {
// Simple implementation for now, just the host.
// We may backoff specific paths (i.e. "pods") differentially
// in the future.
host, err := url.Parse(rawurl.String())
if err != nil {
klog.V(4).Infof("Error extracting url: %v", rawurl)
panic("bad url!")
}
return host.Host
}
// UpdateBackoff updates backoff metadata
func (b *URLBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) {
// range for retry counts that we store is [0,13]
if responseCode > maxResponseCode || serverIsOverloadedSet.Has(responseCode) {
b.Backoff.Next(b.baseUrlKey(actualUrl), b.Backoff.Clock.Now())
return
} else if responseCode >= 300 || err != nil {
klog.V(4).Infof("Client is returning errors: code %v, error %v", responseCode, err)
}
//If we got this far, there is no backoff required for this URL anymore.
b.Backoff.Reset(b.baseUrlKey(actualUrl))
}
// CalculateBackoff takes a url and back's off exponentially,
// based on its knowledge of existing failures.
func (b *URLBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration {
return b.Backoff.Get(b.baseUrlKey(actualUrl))
}
func (b *URLBackoff) Sleep(d time.Duration) {
b.Backoff.Clock.Sleep(d)
}

147
e2e/vendor/k8s.io/client-go/rest/warnings.go generated vendored Normal file
View File

@ -0,0 +1,147 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"io"
"net/http"
"sync"
"k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/util/net"
)
// WarningHandler is an interface for handling warning headers
type WarningHandler interface {
// HandleWarningHeader is called with the warn code, agent, and text when a warning header is countered.
HandleWarningHeader(code int, agent string, text string)
}
var (
defaultWarningHandler WarningHandler = WarningLogger{}
defaultWarningHandlerLock sync.RWMutex
)
// SetDefaultWarningHandler sets the default handler clients use when warning headers are encountered.
// By default, warnings are logged. Several built-in implementations are provided:
// - NoWarnings suppresses warnings.
// - WarningLogger logs warnings.
// - NewWarningWriter() outputs warnings to the provided writer.
func SetDefaultWarningHandler(l WarningHandler) {
defaultWarningHandlerLock.Lock()
defer defaultWarningHandlerLock.Unlock()
defaultWarningHandler = l
}
func getDefaultWarningHandler() WarningHandler {
defaultWarningHandlerLock.RLock()
defer defaultWarningHandlerLock.RUnlock()
l := defaultWarningHandler
return l
}
// NoWarnings is an implementation of WarningHandler that suppresses warnings.
type NoWarnings struct{}
func (NoWarnings) HandleWarningHeader(code int, agent string, message string) {}
// WarningLogger is an implementation of WarningHandler that logs code 299 warnings
type WarningLogger struct{}
func (WarningLogger) HandleWarningHeader(code int, agent string, message string) {
if code != 299 || len(message) == 0 {
return
}
klog.Warning(message)
}
type warningWriter struct {
// out is the writer to output warnings to
out io.Writer
// opts contains options controlling warning output
opts WarningWriterOptions
// writtenLock guards written and writtenCount
writtenLock sync.Mutex
writtenCount int
written map[string]struct{}
}
// WarningWriterOptions controls the behavior of a WarningHandler constructed using NewWarningWriter()
type WarningWriterOptions struct {
// Deduplicate indicates a given warning message should only be written once.
// Setting this to true in a long-running process handling many warnings can result in increased memory use.
Deduplicate bool
// Color indicates that warning output can include ANSI color codes
Color bool
}
// NewWarningWriter returns an implementation of WarningHandler that outputs code 299 warnings to the specified writer.
func NewWarningWriter(out io.Writer, opts WarningWriterOptions) *warningWriter {
h := &warningWriter{out: out, opts: opts}
if opts.Deduplicate {
h.written = map[string]struct{}{}
}
return h
}
const (
yellowColor = "\u001b[33;1m"
resetColor = "\u001b[0m"
)
// HandleWarningHeader prints warnings with code=299 to the configured writer.
func (w *warningWriter) HandleWarningHeader(code int, agent string, message string) {
if code != 299 || len(message) == 0 {
return
}
w.writtenLock.Lock()
defer w.writtenLock.Unlock()
if w.opts.Deduplicate {
if _, alreadyWritten := w.written[message]; alreadyWritten {
return
}
w.written[message] = struct{}{}
}
w.writtenCount++
if w.opts.Color {
fmt.Fprintf(w.out, "%sWarning:%s %s\n", yellowColor, resetColor, message)
} else {
fmt.Fprintf(w.out, "Warning: %s\n", message)
}
}
func (w *warningWriter) WarningCount() int {
w.writtenLock.Lock()
defer w.writtenLock.Unlock()
return w.writtenCount
}
func handleWarnings(headers http.Header, handler WarningHandler) []net.WarningHeader {
if handler == nil {
handler = getDefaultWarningHandler()
}
warnings, _ := net.ParseWarningHeaders(headers["Warning"])
for _, warning := range warnings {
handler.HandleWarningHeader(warning.Code, warning.Agent, warning.Text)
}
return warnings
}

72
e2e/vendor/k8s.io/client-go/rest/watch/decoder.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package watch
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/watch"
)
// Decoder implements the watch.Decoder interface for io.ReadClosers that
// have contents which consist of a series of watchEvent objects encoded
// with the given streaming decoder. The internal objects will be then
// decoded by the embedded decoder.
type Decoder struct {
decoder streaming.Decoder
embeddedDecoder runtime.Decoder
}
// NewDecoder creates an Decoder for the given writer and codec.
func NewDecoder(decoder streaming.Decoder, embeddedDecoder runtime.Decoder) *Decoder {
return &Decoder{
decoder: decoder,
embeddedDecoder: embeddedDecoder,
}
}
// Decode blocks until it can return the next object in the reader. Returns an error
// if the reader is closed or an object can't be decoded.
func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) {
var got metav1.WatchEvent
res, _, err := d.decoder.Decode(nil, &got)
if err != nil {
return "", nil, err
}
if res != &got {
return "", nil, fmt.Errorf("unable to decode to metav1.WatchEvent")
}
switch got.Type {
case string(watch.Added), string(watch.Modified), string(watch.Deleted), string(watch.Error), string(watch.Bookmark):
default:
return "", nil, fmt.Errorf("got invalid watch event type: %v", got.Type)
}
obj, err := runtime.Decode(d.embeddedDecoder, got.Object.Raw)
if err != nil {
return "", nil, fmt.Errorf("unable to decode watch event: %v", err)
}
return watch.EventType(got.Type), obj, nil
}
// Close closes the underlying r.
func (d *Decoder) Close() {
d.decoder.Close()
}

56
e2e/vendor/k8s.io/client-go/rest/watch/encoder.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package watch
import (
"encoding/json"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/watch"
)
// Encoder serializes watch.Events into io.Writer. The internal objects
// are encoded using embedded encoder, and the outer Event is serialized
// using encoder.
// TODO: this type is only used by tests
type Encoder struct {
encoder streaming.Encoder
embeddedEncoder runtime.Encoder
}
func NewEncoder(encoder streaming.Encoder, embeddedEncoder runtime.Encoder) *Encoder {
return &Encoder{
encoder: encoder,
embeddedEncoder: embeddedEncoder,
}
}
// Encode writes an event to the writer. Returns an error
// if the writer is closed or an object can't be encoded.
func (e *Encoder) Encode(event *watch.Event) error {
data, err := runtime.Encode(e.embeddedEncoder, event.Object)
if err != nil {
return err
}
// FIXME: get rid of json.RawMessage.
return e.encoder.Encode(&metav1.WatchEvent{
Type: string(event.Type),
Object: runtime.RawExtension{Raw: json.RawMessage(data)},
})
}

369
e2e/vendor/k8s.io/client-go/rest/with_retry.go generated vendored Normal file
View File

@ -0,0 +1,369 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"time"
"k8s.io/klog/v2"
)
// IsRetryableErrorFunc allows the client to provide its own function
// that determines whether the specified err from the server is retryable.
//
// request: the original request sent to the server
// err: the server sent this error to us
//
// The function returns true if the error is retryable and the request
// can be retried, otherwise it returns false.
// We have four mode of communications - 'Stream', 'Watch', 'Do' and 'DoRaw', this
// function allows us to customize the retryability aspect of each.
type IsRetryableErrorFunc func(request *http.Request, err error) bool
func (r IsRetryableErrorFunc) IsErrorRetryable(request *http.Request, err error) bool {
return r(request, err)
}
var neverRetryError = IsRetryableErrorFunc(func(_ *http.Request, _ error) bool {
return false
})
// WithRetry allows the client to retry a request up to a certain number of times
// Note that WithRetry is not safe for concurrent use by multiple
// goroutines without additional locking or coordination.
type WithRetry interface {
// IsNextRetry advances the retry counter appropriately
// and returns true if the request should be retried,
// otherwise it returns false, if:
// - we have already reached the maximum retry threshold.
// - the error does not fall into the retryable category.
// - the server has not sent us a 429, or 5xx status code and the
// 'Retry-After' response header is not set with a value.
// - we need to seek to the beginning of the request body before we
// initiate the next retry, the function should log an error and
// return false if it fails to do so.
//
// restReq: the associated rest.Request
// httpReq: the HTTP Request sent to the server
// resp: the response sent from the server, it is set if err is nil
// err: the server sent this error to us, if err is set then resp is nil.
// f: a IsRetryableErrorFunc function provided by the client that determines
// if the err sent by the server is retryable.
IsNextRetry(ctx context.Context, restReq *Request, httpReq *http.Request, resp *http.Response, err error, f IsRetryableErrorFunc) bool
// Before should be invoked prior to each attempt, including
// the first one. If an error is returned, the request should
// be aborted immediately.
//
// Before may also be additionally responsible for preparing
// the request for the next retry, namely in terms of resetting
// the request body in case it has been read.
Before(ctx context.Context, r *Request) error
// After should be invoked immediately after an attempt is made.
After(ctx context.Context, r *Request, resp *http.Response, err error)
// WrapPreviousError wraps the error from any previous attempt into
// the final error specified in 'finalErr', so the user has more
// context why the request failed.
// For example, if a request times out after multiple retries then
// we see a generic context.Canceled or context.DeadlineExceeded
// error which is not very useful in debugging. This function can
// wrap any error from previous attempt(s) to provide more context to
// the user. The error returned in 'err' must satisfy the
// following conditions:
// a: errors.Unwrap(err) = errors.Unwrap(finalErr) if finalErr
// implements Unwrap
// b: errors.Unwrap(err) = finalErr if finalErr does not
// implements Unwrap
// c: errors.Is(err, otherErr) = errors.Is(finalErr, otherErr)
WrapPreviousError(finalErr error) (err error)
}
// RetryAfter holds information associated with the next retry.
type RetryAfter struct {
// Wait is the duration the server has asked us to wait before
// the next retry is initiated.
// This is the value of the 'Retry-After' response header in seconds.
Wait time.Duration
// Attempt is the Nth attempt after which we have received a retryable
// error or a 'Retry-After' response header from the server.
Attempt int
// Reason describes why we are retrying the request
Reason string
}
type withRetry struct {
maxRetries int
attempts int
// retry after parameters that pertain to the attempt that is to
// be made soon, so as to enable 'Before' and 'After' to refer
// to the retry parameters.
// - for the first attempt, it will always be nil
// - for consecutive attempts, it is non nil and holds the
// retry after parameters for the next attempt to be made.
retryAfter *RetryAfter
// we keep track of two most recent errors, if the most
// recent attempt is labeled as 'N' then:
// - currentErr represents the error returned by attempt N, it
// can be nil if attempt N did not return an error.
// - previousErr represents an error from an attempt 'M' which
// precedes attempt 'N' (N - M >= 1), it is non nil only when:
// - for a sequence of attempt(s) 1..n (n>1), there
// is an attempt k (k<n) that returned an error.
previousErr, currentErr error
}
func (r *withRetry) trackPreviousError(err error) {
// keep track of two most recent errors
if r.currentErr != nil {
r.previousErr = r.currentErr
}
r.currentErr = err
}
func (r *withRetry) IsNextRetry(ctx context.Context, restReq *Request, httpReq *http.Request, resp *http.Response, err error, f IsRetryableErrorFunc) bool {
defer r.trackPreviousError(err)
if httpReq == nil || (resp == nil && err == nil) {
// bad input, we do nothing.
return false
}
if restReq.body != nil {
// we have an opaque reader, we can't safely reset it
return false
}
r.attempts++
r.retryAfter = &RetryAfter{Attempt: r.attempts}
if r.attempts > r.maxRetries {
return false
}
// if the server returned an error, it takes precedence over the http response.
var errIsRetryable bool
if f != nil && err != nil && f.IsErrorRetryable(httpReq, err) {
errIsRetryable = true
// we have a retryable error, for which we will create an
// artificial "Retry-After" response.
resp = retryAfterResponse()
}
if err != nil && !errIsRetryable {
return false
}
// if we are here, we have either a or b:
// a: we have a retryable error, for which we already
// have an artificial "Retry-After" response.
// b: we have a response from the server for which we
// need to check if it is retryable
seconds, wait := checkWait(resp)
if !wait {
return false
}
r.retryAfter.Wait = time.Duration(seconds) * time.Second
r.retryAfter.Reason = getRetryReason(r.attempts, seconds, resp, err)
return true
}
func (r *withRetry) Before(ctx context.Context, request *Request) error {
// If the request context is already canceled there
// is no need to retry.
if ctx.Err() != nil {
r.trackPreviousError(ctx.Err())
return ctx.Err()
}
url := request.URL()
// r.retryAfter represents the retry after parameters calculated
// from the (response, err) tuple from the last attempt, so 'Before'
// can apply these retry after parameters prior to the next attempt.
// 'r.retryAfter == nil' indicates that this is the very first attempt.
if r.retryAfter == nil {
// we do a backoff sleep before the first attempt is made,
// (preserving current behavior).
if request.backoff != nil {
request.backoff.Sleep(request.backoff.CalculateBackoff(url))
}
return nil
}
// if we are here, we have made attempt(s) at least once before.
if request.backoff != nil {
delay := request.backoff.CalculateBackoff(url)
if r.retryAfter.Wait > delay {
delay = r.retryAfter.Wait
}
request.backoff.Sleep(delay)
}
// We are retrying the request that we already send to
// apiserver at least once before. This request should
// also be throttled with the client-internal rate limiter.
if err := request.tryThrottleWithInfo(ctx, r.retryAfter.Reason); err != nil {
r.trackPreviousError(ctx.Err())
return err
}
klog.V(4).Infof("Got a Retry-After %s response for attempt %d to %v", r.retryAfter.Wait, r.retryAfter.Attempt, request.URL().String())
return nil
}
func (r *withRetry) After(ctx context.Context, request *Request, resp *http.Response, err error) {
// 'After' is invoked immediately after an attempt is made, let's label
// the attempt we have just made as attempt 'N'.
// the current value of r.retryAfter represents the retry after
// parameters calculated from the (response, err) tuple from
// attempt N-1, so r.retryAfter is outdated and should not be
// referred to here.
isRetry := r.retryAfter != nil
r.retryAfter = nil
// the client finishes a single request after N attempts (1..N)
// - all attempts (1..N) are counted to the rest_client_requests_total
// metric (current behavior).
// - every attempt after the first (2..N) are counted to the
// rest_client_request_retries_total metric.
updateRequestResultMetric(ctx, request, resp, err)
if isRetry {
// this is attempt 2 or later
updateRequestRetryMetric(ctx, request, resp, err)
}
if request.c.base != nil {
if err != nil {
request.backoff.UpdateBackoff(request.URL(), err, 0)
} else {
request.backoff.UpdateBackoff(request.URL(), err, resp.StatusCode)
}
}
}
func (r *withRetry) WrapPreviousError(currentErr error) error {
if currentErr == nil || r.previousErr == nil {
return currentErr
}
// if both previous and current error objects represent the error,
// then there is no need to wrap the previous error.
if currentErr.Error() == r.previousErr.Error() {
return currentErr
}
previousErr := r.previousErr
// net/http wraps the underlying error with an url.Error, if the
// previous err object is an instance of url.Error, then we can
// unwrap it to get to the inner error object, this is so we can
// avoid error message like:
// Error: Get "http://foo.bar/api/v1": context deadline exceeded - error \
// from a previous attempt: Error: Get "http://foo.bar/api/v1": EOF
if urlErr, ok := r.previousErr.(*url.Error); ok && urlErr != nil {
if urlErr.Unwrap() != nil {
previousErr = urlErr.Unwrap()
}
}
return &wrapPreviousError{
currentErr: currentErr,
previousError: previousErr,
}
}
type wrapPreviousError struct {
currentErr, previousError error
}
func (w *wrapPreviousError) Unwrap() error { return w.currentErr }
func (w *wrapPreviousError) Error() string {
return fmt.Sprintf("%s - error from a previous attempt: %s", w.currentErr.Error(), w.previousError.Error())
}
// checkWait returns true along with a number of seconds if
// the server instructed us to wait before retrying.
func checkWait(resp *http.Response) (int, bool) {
switch r := resp.StatusCode; {
// any 500 error code and 429 can trigger a wait
case r == http.StatusTooManyRequests, r >= 500:
default:
return 0, false
}
i, ok := retryAfterSeconds(resp)
return i, ok
}
func getRetryReason(retries, seconds int, resp *http.Response, err error) string {
// priority and fairness sets the UID of the FlowSchema
// associated with a request in the following response Header.
const responseHeaderMatchedFlowSchemaUID = "X-Kubernetes-PF-FlowSchema-UID"
message := fmt.Sprintf("retries: %d, retry-after: %ds", retries, seconds)
switch {
case resp.StatusCode == http.StatusTooManyRequests:
// it is server-side throttling from priority and fairness
flowSchemaUID := resp.Header.Get(responseHeaderMatchedFlowSchemaUID)
return fmt.Sprintf("%s - retry-reason: due to server-side throttling, FlowSchema UID: %q", message, flowSchemaUID)
case err != nil:
// it's a retryable error
return fmt.Sprintf("%s - retry-reason: due to retryable error, error: %v", message, err)
default:
return fmt.Sprintf("%s - retry-reason: %d", message, resp.StatusCode)
}
}
func readAndCloseResponseBody(resp *http.Response) {
if resp == nil {
return
}
// Ensure the response body is fully read and closed
// before we reconnect, so that we reuse the same TCP
// connection.
const maxBodySlurpSize = 2 << 10
defer resp.Body.Close()
if resp.ContentLength <= maxBodySlurpSize {
io.Copy(io.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
}
}
func retryAfterResponse() *http.Response {
return retryAfterResponseWithDelay("1")
}
func retryAfterResponseWithDelay(delay string) *http.Response {
return retryAfterResponseWithCodeAndDelay(http.StatusInternalServerError, delay)
}
func retryAfterResponseWithCodeAndDelay(code int, delay string) *http.Response {
return &http.Response{
StatusCode: code,
Header: http.Header{"Retry-After": []string{delay}},
}
}

View File

@ -0,0 +1,58 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package rest
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) {
*out = *in
if in.CertData != nil {
in, out := &in.CertData, &out.CertData
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.KeyData != nil {
in, out := &in.KeyData, &out.KeyData
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CAData != nil {
in, out := &in.CAData, &out.CAData
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.NextProtos != nil {
in, out := &in.NextProtos, &out.NextProtos
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientConfig.
func (in *TLSClientConfig) DeepCopy() *TLSClientConfig {
if in == nil {
return nil
}
out := new(TLSClientConfig)
in.DeepCopyInto(out)
return out
}