reconcile merge

Signed-off-by: Huamin Chen <hchen@redhat.com>
This commit is contained in:
Huamin Chen
2019-01-15 16:20:41 +00:00
parent 85b8415024
commit e46099a504
2425 changed files with 271763 additions and 40453 deletions

98
vendor/k8s.io/client-go/rest/BUILD generated vendored
View File

@ -1,98 +0,0 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"client_test.go",
"config_test.go",
"plugin_test.go",
"request_test.go",
"url_utils_test.go",
"urlbackoff_test.go",
],
embed = [":go_default_library"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/httpstream:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//vendor/k8s.io/client-go/rest/watch:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"client.go",
"config.go",
"plugin.go",
"request.go",
"transport.go",
"url_utils.go",
"urlbackoff.go",
"zz_generated.deepcopy.go",
],
importpath = "k8s.io/client-go/rest",
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/http2:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/pkg/version:go_default_library",
"//vendor/k8s.io/client-go/rest/watch:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
"//vendor/k8s.io/client-go/tools/metrics:go_default_library",
"//vendor/k8s.io/client-go/transport:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//staging/src/k8s.io/client-go/rest/fake:all-srcs",
"//staging/src/k8s.io/client-go/rest/watch:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -17,6 +17,8 @@ limitations under the License.
package rest
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
@ -27,9 +29,6 @@ import (
"strings"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -37,6 +36,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
certutil "k8s.io/client-go/util/cert"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog"
)
const (
@ -44,6 +44,8 @@ const (
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 {
@ -77,6 +79,9 @@ type Config struct {
// Callback to persist config for AuthProvider.
AuthConfigPersister AuthProviderConfigPersister
// Exec-based authentication provider.
ExecProvider *clientcmdapi.ExecConfig
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
@ -108,7 +113,7 @@ type Config struct {
Timeout time.Duration
// Dial specifies the dial function for creating unencrypted TCP connections.
Dial func(network, addr string) (net.Conn, error)
Dial func(ctx context.Context, network, address string) (net.Conn, error)
// Version forces a specific version to be used (if registered)
// Do we need this?
@ -217,7 +222,7 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
// the config.Version to be empty.
func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
if config.NegotiatedSerializer == nil {
return nil, fmt.Errorf("NeogitatedSerializer is required when initializing a RESTClient")
return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
}
baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
@ -305,22 +310,28 @@ func DefaultKubernetesUserAgent() string {
// 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 an error if
// called from a process not running in a kubernetes environment.
// 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, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
return nil, ErrNotInCluster
}
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + v1.ServiceAccountTokenKey)
if err != nil {
ts := NewCachedFileTokenSource(tokenFile)
if _, err := ts.Token(); err != nil {
return nil, err
}
tlsClientConfig := TLSClientConfig{}
rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + v1.ServiceAccountRootCAKey
if _, err := certutil.NewPool(rootCAFile); err != nil {
glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
} else {
tlsClientConfig.CAFile = rootCAFile
}
@ -328,8 +339,8 @@ func InClusterConfig() (*Config, error) {
return &Config{
// TODO: switch to using cluster DNS.
Host: "https://" + net.JoinHostPort(host, port),
BearerToken: string(token),
TLSClientConfig: tlsClientConfig,
WrapTransport: TokenSourceWrapTransport(ts),
}, nil
}
@ -432,6 +443,7 @@ func CopyConfig(config *Config) *Config {
},
AuthProvider: config.AuthProvider,
AuthConfigPersister: config.AuthConfigPersister,
ExecProvider: config.ExecProvider,
TLSClientConfig: TLSClientConfig{
Insecure: config.TLSClientConfig.Insecure,
ServerName: config.TLSClientConfig.ServerName,

View File

@ -17,6 +17,8 @@ limitations under the License.
package rest
import (
"context"
"errors"
"io"
"net"
"net/http"
@ -25,8 +27,6 @@ import (
"strings"
"testing"
fuzz "github.com/google/gofuzz"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -35,8 +35,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/util/flowcontrol"
"errors"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
)
@ -208,7 +207,7 @@ func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder,
return &fakeCodec{}
}
var fakeDialFunc = func(network, addr string) (net.Conn, error) {
var fakeDialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return nil, fakeDialerError
}
var fakeDialerError = errors.New("fakedialer")
@ -253,7 +252,7 @@ func TestAnonymousConfig(t *testing.T) {
r.Config = map[string]string{}
},
// Dial does not require fuzzer
func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) {},
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {},
)
for i := 0; i < 20; i++ {
original := &Config{}
@ -269,6 +268,7 @@ func TestAnonymousConfig(t *testing.T) {
expected.Password = ""
expected.AuthProvider = nil
expected.AuthConfigPersister = nil
expected.ExecProvider = nil
expected.TLSClientConfig.CertData = nil
expected.TLSClientConfig.CertFile = ""
expected.TLSClientConfig.KeyData = nil
@ -283,10 +283,10 @@ func TestAnonymousConfig(t *testing.T) {
expected.WrapTransport = nil
}
if actual.Dial != nil {
_, actualError := actual.Dial("", "")
_, expectedError := actual.Dial("", "")
_, actualError := actual.Dial(context.Background(), "", "")
_, expectedError := expected.Dial(context.Background(), "", "")
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
t.Fatalf("CopyConfig dropped the Dial field")
}
} else {
actual.Dial = nil
@ -328,7 +328,7 @@ func TestCopyConfig(t *testing.T) {
func(r *AuthProviderConfigPersister, f fuzz.Continue) {
*r = fakeAuthProviderConfigPersister{}
},
func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) {
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
*r = fakeDialFunc
},
)
@ -350,8 +350,8 @@ func TestCopyConfig(t *testing.T) {
expected.WrapTransport = nil
}
if actual.Dial != nil {
_, actualError := actual.Dial("", "")
_, expectedError := actual.Dial("", "")
_, actualError := actual.Dial(context.Background(), "", "")
_, expectedError := expected.Dial(context.Background(), "", "")
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
}
@ -360,7 +360,7 @@ func TestCopyConfig(t *testing.T) {
expected.Dial = nil
if actual.AuthConfigPersister != nil {
actualError := actual.AuthConfigPersister.Persist(nil)
expectedError := actual.AuthConfigPersister.Persist(nil)
expectedError := expected.AuthConfigPersister.Persist(nil)
if !reflect.DeepEqual(expectedError, actualError) {
t.Fatalf("CopyConfig dropped the Dial field")
}

View File

@ -1,32 +0,0 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["fake.go"],
importpath = "k8s.io/client-go/rest/fake",
deps = [
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -94,14 +94,10 @@ func (c *RESTClient) request(verb string) *restclient.Request {
ns := c.NegotiatedSerializer
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
internalVersion := schema.GroupVersion{
Group: c.GroupVersion.Group,
Version: runtime.APIVersionInternal,
}
serializers := restclient.Serializers{
// TODO this was hardcoded before, but it doesn't look right
Encoder: ns.EncoderForVersion(info.Serializer, c.GroupVersion),
Decoder: ns.DecoderToVersion(info.Serializer, internalVersion),
Decoder: ns.DecoderToVersion(info.Serializer, c.GroupVersion),
}
if info.StreamSerializer != nil {
serializers.StreamingSerializer = info.StreamSerializer.Serializer

View File

@ -21,7 +21,7 @@ import (
"net/http"
"sync"
"github.com/golang/glog"
"k8s.io/klog"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
@ -57,7 +57,7 @@ func RegisterAuthProviderPlugin(name string, plugin Factory) error {
if _, found := plugins[name]; found {
return fmt.Errorf("Auth Provider Plugin %q was registered twice", name)
}
glog.V(4).Infof("Registered Auth Provider Plugin %q", name)
klog.V(4).Infof("Registered Auth Provider Plugin %q", name)
plugins[name] = plugin
return nil
}

View File

@ -32,7 +32,6 @@ import (
"strings"
"time"
"github.com/golang/glog"
"golang.org/x/net/http2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -44,6 +43,7 @@ import (
restclientwatch "k8s.io/client-go/rest/watch"
"k8s.io/client-go/tools/metrics"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog"
)
var (
@ -114,7 +114,7 @@ type Request struct {
// NewRequest creates a new request helper object for accessing runtime.Objects on a server.
func NewRequest(client HTTPClient, verb string, baseURL *url.URL, versionedAPIPath string, content ContentConfig, serializers Serializers, backoff BackoffManager, throttle flowcontrol.RateLimiter, timeout time.Duration) *Request {
if backoff == nil {
glog.V(2).Infof("Not implementing request backoff strategy.")
klog.V(2).Infof("Not implementing request backoff strategy.")
backoff = &NoBackoff{}
}
@ -198,7 +198,7 @@ func (r *Request) Throttle(limiter flowcontrol.RateLimiter) *Request {
return r
}
// SubResource sets a sub-resource path which can be multiple segments segment after the resource
// SubResource sets a sub-resource path which can be multiple segments after the resource
// name but before the suffix.
func (r *Request) SubResource(subresources ...string) *Request {
if r.err != nil {
@ -317,10 +317,14 @@ func (r *Request) Param(paramName, s string) *Request {
// VersionedParams will not write query parameters that have omitempty set and are empty. If a
// parameter has already been set it is appended to (Params and VersionedParams are additive).
func (r *Request) VersionedParams(obj runtime.Object, codec runtime.ParameterCodec) *Request {
return r.SpecificallyVersionedParams(obj, codec, *r.content.GroupVersion)
}
func (r *Request) SpecificallyVersionedParams(obj runtime.Object, codec runtime.ParameterCodec, version schema.GroupVersion) *Request {
if r.err != nil {
return r
}
params, err := codec.EncodeParameters(obj, *r.content.GroupVersion)
params, err := codec.EncodeParameters(obj, version)
if err != nil {
r.err = err
return r
@ -353,8 +357,8 @@ func (r *Request) SetHeader(key string, values ...string) *Request {
return r
}
// Timeout makes the request use the given duration as a timeout. Sets the "timeout"
// parameter.
// Timeout makes the request use the given duration as an overall timeout for the
// request. Additionally, if set passes the value as "timeout" parameter in URL.
func (r *Request) Timeout(d time.Duration) *Request {
if r.err != nil {
return r
@ -451,17 +455,9 @@ func (r *Request) URL() *url.URL {
// finalURLTemplate is similar to URL(), but will make all specific parameter values equal
// - instead of name or namespace, "{name}" and "{namespace}" will be used, and all query
// parameters will be reset. This creates a copy of the request so as not to change the
// underlying object. This means some useful request info (like the types of field
// selectors in use) will be lost.
// TODO: preserve field selector keys
// parameters will be reset. This creates a copy of the url so as not to change the
// underlying object.
func (r Request) finalURLTemplate() url.URL {
if len(r.resourceName) != 0 {
r.resourceName = "{name}"
}
if r.namespaceSet && len(r.namespace) != 0 {
r.namespace = "{namespace}"
}
newParams := url.Values{}
v := []string{"{value}"}
for k := range r.params {
@ -469,6 +465,59 @@ func (r Request) finalURLTemplate() url.URL {
}
r.params = newParams
url := r.URL()
segments := strings.Split(r.URL().Path, "/")
groupIndex := 0
index := 0
if r.URL() != nil && r.baseURL != nil && strings.Contains(r.URL().Path, r.baseURL.Path) {
groupIndex += len(strings.Split(r.baseURL.Path, "/"))
}
if groupIndex >= len(segments) {
return *url
}
const CoreGroupPrefix = "api"
const NamedGroupPrefix = "apis"
isCoreGroup := segments[groupIndex] == CoreGroupPrefix
isNamedGroup := segments[groupIndex] == NamedGroupPrefix
if isCoreGroup {
// checking the case of core group with /api/v1/... format
index = groupIndex + 2
} else if isNamedGroup {
// checking the case of named group with /apis/apps/v1/... format
index = groupIndex + 3
} else {
// this should not happen that the only two possibilities are /api... and /apis..., just want to put an
// outlet here in case more API groups are added in future if ever possible:
// https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups
// if a wrong API groups name is encountered, return the {prefix} for url.Path
url.Path = "/{prefix}"
url.RawQuery = ""
return *url
}
//switch segLength := len(segments) - index; segLength {
switch {
// case len(segments) - index == 1:
// resource (with no name) do nothing
case len(segments)-index == 2:
// /$RESOURCE/$NAME: replace $NAME with {name}
segments[index+1] = "{name}"
case len(segments)-index == 3:
if segments[index+2] == "finalize" || segments[index+2] == "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+1] = "{name}"
} else {
// /namespace/$NAMESPACE/$RESOURCE: replace $NAMESPACE with {namespace}
segments[index+1] = "{namespace}"
}
case len(segments)-index >= 4:
segments[index+1] = "{namespace}"
// /namespace/$NAMESPACE/$RESOURCE/$NAME: replace $NAMESPACE with {namespace}, $NAME with {name}
if segments[index+3] != "finalize" && segments[index+3] != "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+3] = "{name}"
}
}
url.Path = path.Join(segments...)
return *url
}
@ -478,13 +527,26 @@ func (r *Request) tryThrottle() {
r.throttle.Accept()
}
if latency := time.Since(now); latency > longThrottleLatency {
glog.V(4).Infof("Throttling request took %v, request: %s:%s", latency, r.verb, r.URL().String())
klog.V(4).Infof("Throttling request took %v, request: %s:%s", latency, r.verb, r.URL().String())
}
}
// Watch attempts to begin watching the requested location.
// Returns a watch.Interface, or an error.
func (r *Request) Watch() (watch.Interface, error) {
return r.WatchWithSpecificDecoders(
func(body io.ReadCloser) streaming.Decoder {
framer := r.serializers.Framer.NewFrameReader(body)
return streaming.NewDecoder(framer, r.serializers.StreamingSerializer)
},
r.serializers.Decoder,
)
}
// WatchWithSpecificDecoders attempts to begin watching the requested location with a *different* decoder.
// Turns out that you want one "standard" decoder for the watch event and one "personal" decoder for the content
// Returns a watch.Interface, or an error.
func (r *Request) WatchWithSpecificDecoders(wrapperDecoderFn func(io.ReadCloser) streaming.Decoder, embeddedDecoder runtime.Decoder) (watch.Interface, error) {
// We specifically don't want to rate limit watches, so we
// don't use r.throttle here.
if r.err != nil {
@ -532,9 +594,8 @@ func (r *Request) Watch() (watch.Interface, error) {
}
return nil, fmt.Errorf("for request '%+v', got status: %v", url, resp.StatusCode)
}
framer := r.serializers.Framer.NewFrameReader(resp.Body)
decoder := streaming.NewDecoder(framer, r.serializers.StreamingSerializer)
return watch.NewStreamWatcher(restclientwatch.NewDecoder(decoder, r.serializers.Decoder)), nil
wrapperDecoder := wrapperDecoderFn(resp.Body)
return watch.NewStreamWatcher(restclientwatch.NewDecoder(wrapperDecoder, embeddedDecoder)), nil
}
// updateURLMetrics is a convenience function for pushing metrics.
@ -622,7 +683,7 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
}()
if r.err != nil {
glog.V(4).Infof("Error in request: %v", r.err)
klog.V(4).Infof("Error in request: %v", r.err)
return r.err
}
@ -640,7 +701,6 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
}
// Right now we make about ten retry attempts if we get a Retry-After response.
// TODO: Change to a timeout based approach.
maxRetries := 10
retries := 0
for {
@ -649,6 +709,14 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
if err != nil {
return err
}
if r.timeout > 0 {
if r.ctx == nil {
r.ctx = context.Background()
}
var cancelFn context.CancelFunc
r.ctx, cancelFn = context.WithTimeout(r.ctx, r.timeout)
defer cancelFn()
}
if r.ctx != nil {
req = req.WithContext(r.ctx)
}
@ -702,13 +770,13 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
if seeker, ok := r.body.(io.Seeker); ok && r.body != nil {
_, err := seeker.Seek(0, 0)
if err != nil {
glog.V(4).Infof("Could not retry request, can't Seek() back to beginning of body for %T", r.body)
klog.V(4).Infof("Could not retry request, can't Seek() back to beginning of body for %T", r.body)
fn(req, resp)
return true
}
}
glog.V(4).Infof("Got a Retry-After %s response for attempt %d to %v", seconds, retries, url)
klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v", seconds, retries, url)
r.backoffMgr.Sleep(time.Duration(seconds) * time.Second)
return false
}
@ -776,13 +844,13 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu
// 2. Apiserver sends back the headers and then part of the body
// 3. Apiserver closes connection.
// 4. client-go should catch this and return an error.
glog.V(2).Infof("Stream error %#v when reading response body, may be caused by closed connection.", err)
klog.V(2).Infof("Stream error %#v when reading response body, may be caused by closed connection.", err)
streamErr := fmt.Errorf("Stream error %#v when reading response body, may be caused by closed connection. Please retry.", err)
return Result{
err: streamErr,
}
default:
glog.Errorf("Unexpected error when reading response body: %#v", err)
klog.Errorf("Unexpected error when reading response body: %#v", err)
unexpectedErr := fmt.Errorf("Unexpected error %#v when reading response body. Please retry.", err)
return Result{
err: unexpectedErr,
@ -846,11 +914,11 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu
func truncateBody(body string) string {
max := 0
switch {
case bool(glog.V(10)):
case bool(klog.V(10)):
return body
case bool(glog.V(9)):
case bool(klog.V(9)):
max = 10240
case bool(glog.V(8)):
case bool(klog.V(8)):
max = 1024
}
@ -865,13 +933,13 @@ func truncateBody(body string) string {
// allocating a new string for the body output unless necessary. Uses a simple heuristic to determine
// whether the body is printable.
func glogBody(prefix string, body []byte) {
if glog.V(8) {
if klog.V(8) {
if bytes.IndexFunc(body, func(r rune) bool {
return r < 0x0a
}) != -1 {
glog.Infof("%s:\n%s", prefix, truncateBody(hex.Dump(body)))
klog.Infof("%s:\n%s", prefix, truncateBody(hex.Dump(body)))
} else {
glog.Infof("%s: %s", prefix, truncateBody(string(body)))
klog.Infof("%s: %s", prefix, truncateBody(string(body)))
}
}
}
@ -1073,7 +1141,7 @@ func (r Result) Error() error {
// to be backwards compatible with old servers that do not return a version, default to "v1"
out, _, err := r.decoder.Decode(r.body, &schema.GroupVersionKind{Version: "v1"}, nil)
if err != nil {
glog.V(5).Infof("body was not decodable (unable to check for Status): %v", err)
klog.V(5).Infof("body was not decodable (unable to check for Status): %v", err)
return r.err
}
switch t := out.(type) {

View File

@ -35,7 +35,7 @@ import (
"testing"
"time"
"github.com/golang/glog"
"k8s.io/klog"
"k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
@ -340,21 +340,169 @@ func TestResultIntoWithNoBodyReturnsErr(t *testing.T) {
}
func TestURLTemplate(t *testing.T) {
uri, _ := url.Parse("http://localhost")
r := NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0)
r.Prefix("pre1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0")
full := r.URL()
if full.String() != "http://localhost/pre1/namespaces/ns/r1/nm?p0=v0" {
t.Errorf("unexpected initial URL: %s", full)
uri, _ := url.Parse("http://localhost/some/base/url/path")
testCases := []struct {
Request *Request
ExpectedFullURL string
ExpectedFinalURL string
}{
{
// non dynamic client
Request: NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("api", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D",
},
{
// non dynamic client with wrong api group
Request: NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("pre1", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with NO name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with core group + resourceResource (with name)
// /api/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/status",
},
{
// dynamic client with named group + namespace + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/status",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
},
{
// dynamic client with wrong api group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
}
actualURL := r.finalURLTemplate()
actual := actualURL.String()
expected := "http://localhost/pre1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D"
if actual != expected {
t.Errorf("unexpected URL template: %s %s", actual, expected)
}
if r.URL().String() != full.String() {
t.Errorf("creating URL template changed request: %s -> %s", full.String(), r.URL().String())
for i, testCase := range testCases {
r := testCase.Request
full := r.URL()
if full.String() != testCase.ExpectedFullURL {
t.Errorf("%d: unexpected initial URL: %s %s", i, full, testCase.ExpectedFullURL)
}
actualURL := r.finalURLTemplate()
actual := actualURL.String()
if actual != testCase.ExpectedFinalURL {
t.Errorf("%d: unexpected URL template: %s %s", i, actual, testCase.ExpectedFinalURL)
}
if r.URL().String() != full.String() {
t.Errorf("%d, creating URL template changed request: %s -> %s", i, full.String(), r.URL().String())
}
}
}
@ -1522,7 +1670,7 @@ func TestBody(t *testing.T) {
f.Close()
defer os.Remove(f.Name())
var nilObject *v1.DeleteOptions
var nilObject *metav1.DeleteOptions
typedObject := interface{}(nilObject)
c := testRESTClient(t, nil)
tests := []struct {
@ -1707,6 +1855,10 @@ func buildString(length int) string {
return string(s)
}
func init() {
klog.InitFlags(nil)
}
func TestTruncateBody(t *testing.T) {
tests := []struct {
body string
@ -1756,7 +1908,7 @@ func TestTruncateBody(t *testing.T) {
},
}
l := flag.Lookup("v").Value.(flag.Getter).Get().(glog.Level)
l := flag.Lookup("v").Value.(flag.Getter).Get().(klog.Level)
for _, test := range tests {
flag.Set("v", test.level)
got := truncateBody(test.body)

140
vendor/k8s.io/client-go/rest/token_source.go generated vendored Normal file
View File

@ -0,0 +1,140 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
"golang.org/x/oauth2"
"k8s.io/klog"
)
// TokenSourceWrapTransport returns a WrapTransport that injects bearer tokens
// authentication from an oauth2.TokenSource.
func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) http.RoundTripper {
return func(rt http.RoundTripper) http.RoundTripper {
return &tokenSourceTransport{
base: rt,
ort: &oauth2.Transport{
Source: ts,
Base: rt,
},
}
}
}
// NewCachedFileTokenSource returns a oauth2.TokenSource reads a token from a
// file at a specified path and periodically reloads it.
func NewCachedFileTokenSource(path string) oauth2.TokenSource {
return &cachingTokenSource{
now: time.Now,
leeway: 1 * time.Minute,
base: &fileTokenSource{
path: path,
// This period was picked because it is half of the minimum validity
// duration for a token provisioned by they TokenRequest API. This is
// unsophisticated and should induce rotation at a frequency that should
// work with the token volume source.
period: 5 * time.Minute,
},
}
}
type tokenSourceTransport struct {
base http.RoundTripper
ort http.RoundTripper
}
func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// This is to allow --token to override other bearer token providers.
if req.Header.Get("Authorization") != "" {
return tst.base.RoundTrip(req)
}
return tst.ort.RoundTrip(req)
}
type fileTokenSource struct {
path string
period time.Duration
}
var _ = oauth2.TokenSource(&fileTokenSource{})
func (ts *fileTokenSource) Token() (*oauth2.Token, error) {
tokb, err := ioutil.ReadFile(ts.path)
if err != nil {
return nil, fmt.Errorf("failed to read token file %q: %v", ts.path, err)
}
tok := strings.TrimSpace(string(tokb))
if len(tok) == 0 {
return nil, fmt.Errorf("read empty token from file %q", ts.path)
}
return &oauth2.Token{
AccessToken: tok,
Expiry: time.Now().Add(ts.period),
}, nil
}
type cachingTokenSource struct {
base oauth2.TokenSource
leeway time.Duration
sync.RWMutex
tok *oauth2.Token
// for testing
now func() time.Time
}
var _ = oauth2.TokenSource(&cachingTokenSource{})
func (ts *cachingTokenSource) Token() (*oauth2.Token, error) {
now := ts.now()
// fast path
ts.RLock()
tok := ts.tok
ts.RUnlock()
if tok != nil && tok.Expiry.Add(-1*ts.leeway).After(now) {
return tok, nil
}
// slow path
ts.Lock()
defer ts.Unlock()
if tok := ts.tok; tok != nil && tok.Expiry.Add(-1*ts.leeway).After(now) {
return tok, nil
}
tok, err := ts.base.Token()
if err != nil {
if ts.tok == nil {
return nil, err
}
klog.Errorf("Unable to rotate token: %v", err)
return ts.tok, nil
}
ts.tok = tok
return tok, nil
}

156
vendor/k8s.io/client-go/rest/token_source_test.go generated vendored Normal file
View File

@ -0,0 +1,156 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"fmt"
"reflect"
"sync"
"testing"
"time"
"golang.org/x/oauth2"
)
type testTokenSource struct {
calls int
tok *oauth2.Token
err error
}
func (ts *testTokenSource) Token() (*oauth2.Token, error) {
ts.calls++
return ts.tok, ts.err
}
func TestCachingTokenSource(t *testing.T) {
start := time.Now()
tokA := &oauth2.Token{
AccessToken: "a",
Expiry: start.Add(10 * time.Minute),
}
tokB := &oauth2.Token{
AccessToken: "b",
Expiry: start.Add(20 * time.Minute),
}
tests := []struct {
name string
tok *oauth2.Token
tsTok *oauth2.Token
tsErr error
wait time.Duration
wantTok *oauth2.Token
wantErr bool
wantTSCalls int
}{
{
name: "valid token returned from cache",
tok: tokA,
wantTok: tokA,
},
{
name: "valid token returned from cache 1 minute before scheduled refresh",
tok: tokA,
wait: 8 * time.Minute,
wantTok: tokA,
},
{
name: "new token created when cache is empty",
tsTok: tokA,
wantTok: tokA,
wantTSCalls: 1,
},
{
name: "new token created 1 minute after scheduled refresh",
tok: tokA,
tsTok: tokB,
wait: 10 * time.Minute,
wantTok: tokB,
wantTSCalls: 1,
},
{
name: "error on create token returns error",
tsErr: fmt.Errorf("error"),
wantErr: true,
wantTSCalls: 1,
},
}
for _, c := range tests {
t.Run(c.name, func(t *testing.T) {
tts := &testTokenSource{
tok: c.tsTok,
err: c.tsErr,
}
ts := &cachingTokenSource{
base: tts,
tok: c.tok,
leeway: 1 * time.Minute,
now: func() time.Time { return start.Add(c.wait) },
}
gotTok, gotErr := ts.Token()
if got, want := gotTok, c.wantTok; !reflect.DeepEqual(got, want) {
t.Errorf("unexpected token:\n\tgot:\t%#v\n\twant:\t%#v", got, want)
}
if got, want := tts.calls, c.wantTSCalls; got != want {
t.Errorf("unexpected number of Token() calls: got %d, want %d", got, want)
}
if gotErr == nil && c.wantErr {
t.Errorf("wanted error but got none")
}
if gotErr != nil && !c.wantErr {
t.Errorf("unexpected error: %v", gotErr)
}
})
}
}
func TestCachingTokenSourceRace(t *testing.T) {
for i := 0; i < 100; i++ {
tts := &testTokenSource{
tok: &oauth2.Token{
AccessToken: "a",
Expiry: time.Now().Add(1000 * time.Hour),
},
}
ts := &cachingTokenSource{
now: time.Now,
base: tts,
leeway: 1 * time.Minute,
}
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
if _, err := ts.Token(); err != nil {
t.Fatalf("err: %v", err)
}
}()
}
wg.Wait()
if tts.calls != 1 {
t.Errorf("expected one call to Token() but saw: %d", tts.calls)
}
}
}

View File

@ -18,8 +18,10 @@ package rest
import (
"crypto/tls"
"errors"
"net/http"
"k8s.io/client-go/plugin/pkg/client/auth/exec"
"k8s.io/client-go/transport"
)
@ -58,25 +60,10 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
// TransportConfig converts a client config to an appropriate transport config.
func (c *Config) TransportConfig() (*transport.Config, error) {
wt := c.WrapTransport
if c.AuthProvider != nil {
provider, err := GetAuthProvider(c.Host, c.AuthProvider, c.AuthConfigPersister)
if err != nil {
return nil, err
}
if wt != nil {
previousWT := wt
wt = func(rt http.RoundTripper) http.RoundTripper {
return provider.WrapTransport(previousWT(rt))
}
} else {
wt = provider.WrapTransport
}
}
return &transport.Config{
conf := &transport.Config{
UserAgent: c.UserAgent,
Transport: c.Transport,
WrapTransport: wt,
WrapTransport: c.WrapTransport,
TLS: transport.TLSConfig{
Insecure: c.Insecure,
ServerName: c.ServerName,
@ -96,5 +83,34 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
Extra: c.Impersonate.Extra,
},
Dial: c.Dial,
}, nil
}
if c.ExecProvider != nil && c.AuthProvider != nil {
return nil, errors.New("execProvider and authProvider cannot be used in combination")
}
if c.ExecProvider != nil {
provider, err := exec.GetAuthenticator(c.ExecProvider)
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
}
wt := conf.WrapTransport
if wt != nil {
conf.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
return provider.WrapTransport(wt(rt))
}
} else {
conf.WrapTransport = provider.WrapTransport
}
}
return conf, nil
}

View File

@ -20,9 +20,9 @@ import (
"net/url"
"time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog"
)
// Set of resp. Codes that we backoff for.
@ -64,7 +64,7 @@ func (n *NoBackoff) Sleep(d time.Duration) {
// 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() {
glog.V(4).Infof("Disabling backoff strategy")
klog.V(4).Infof("Disabling backoff strategy")
b.Backoff = flowcontrol.NewBackOff(0*time.Second, 0*time.Second)
}
@ -76,7 +76,7 @@ func (b *URLBackoff) baseUrlKey(rawurl *url.URL) string {
// in the future.
host, err := url.Parse(rawurl.String())
if err != nil {
glog.V(4).Infof("Error extracting url: %v", rawurl)
klog.V(4).Infof("Error extracting url: %v", rawurl)
panic("bad url!")
}
return host.Host
@ -89,7 +89,7 @@ func (b *URLBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode i
b.Backoff.Next(b.baseUrlKey(actualUrl), b.Backoff.Clock.Now())
return
} else if responseCode >= 300 || err != nil {
glog.V(4).Infof("Client is returning errors: code %v, error %v", responseCode, err)
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.

View File

@ -1,56 +0,0 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"decoder.go",
"encoder.go",
],
importpath = "k8s.io/client-go/rest/watch",
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = [
"decoder_test.go",
"encoder_test.go",
],
deps = [
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//vendor/k8s.io/client-go/rest/watch:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -1,7 +1,7 @@
// +build !ignore_autogenerated
/*
Copyright 2018 The Kubernetes Authors.
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.