wire config flags through factory

This commit is contained in:
juanvallejo
2018-04-24 19:47:03 -04:00
parent 46e827a8a5
commit 3ca222b2d9
25 changed files with 325 additions and 208 deletions

View File

@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"cached_discovery.go",
"config_flags.go",
"conversion.go",
"factory.go",
"factory_builder.go",
@@ -30,10 +31,10 @@ go_library(
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/cmd/util/openapi/validation:go_default_library",
"//pkg/kubectl/cmd/util/transport:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/transport:go_default_library",
"//pkg/kubectl/validation:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
@@ -139,6 +140,7 @@ filegroup(
"//pkg/kubectl/cmd/util/jsonmerge:all-srcs",
"//pkg/kubectl/cmd/util/openapi:all-srcs",
"//pkg/kubectl/cmd/util/sanity:all-srcs",
"//pkg/kubectl/cmd/util/transport:all-srcs",
],
tags = ["automanaged"],
visibility = ["//build/visible_to:pkg_kubectl_cmd_util_CONSUMERS"],

View File

@@ -22,27 +22,70 @@ import (
"path/filepath"
"time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/kubernetes/pkg/kubectl/util/transport"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/transport"
)
const (
flagClusterName = "cluster"
flagAuthInfoName = "user"
flagContext = "context"
flagNamespace = "namespace"
flagAPIServer = "server"
flagInsecure = "insecure-skip-tls-verify"
flagCertFile = "client-certificate"
flagKeyFile = "client-key"
flagCAFile = "certificate-authority"
flagBearerToken = "token"
flagImpersonate = "as"
flagImpersonateGroup = "as-group"
flagUsername = "username"
flagPassword = "password"
flagTimeout = "request-timeout"
)
// TODO(juanvallejo): move to pkg/kubectl/genericclioptions once
// the dependency on cmdutil is broken here.
// ConfigFlags composes the set of values necessary
// for obtaining a REST client config
type ConfigFlags struct {
CacheDir *string
KubeConfig *string
// config flags
ClusterName *string
AuthInfoName *string
Context *string
Namespace *string
APIServer *string
Insecure *bool
CertFile *string
KeyFile *string
CAFile *string
BearerToken *string
Impersonate *string
ImpersonateGroup *[]string
Username *string
Password *string
Timeout *string
}
// ToRESTConfig returns a REST client configuration based on a provided
// path to a .kubeconfig file, loading rules, and config flag overrides.
// ToRESTConfig implements RESTClientGetter.
// Returns a REST client configuration based on a provided path
// to a .kubeconfig file, loading rules, and config flag overrides.
// Expects the AddFlags method to have been called.
func (f *ConfigFlags) ToRESTConfig() (*rest.Config, error) {
return f.ToRawKubeConfigLoader().ClientConfig()
}
func (f *ConfigFlags) ToRawKubeConfigLoader() clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// use the standard defaults for this client command
// DEPRECATED: remove and replace with something more accurate
@@ -53,13 +96,74 @@ func (f *ConfigFlags) ToRESTConfig() (*rest.Config, error) {
}
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
return clientConfig.ClientConfig()
// bind auth info flag values to overrides
if f.CertFile != nil {
overrides.AuthInfo.ClientCertificate = *f.CertFile
}
if f.KeyFile != nil {
overrides.AuthInfo.ClientKey = *f.KeyFile
}
if f.BearerToken != nil {
overrides.AuthInfo.Token = *f.BearerToken
}
if f.Impersonate != nil {
overrides.AuthInfo.Impersonate = *f.Impersonate
}
if f.ImpersonateGroup != nil {
overrides.AuthInfo.ImpersonateGroups = *f.ImpersonateGroup
}
if f.Username != nil {
overrides.AuthInfo.Username = *f.Username
}
if f.Password != nil {
overrides.AuthInfo.Password = *f.Password
}
// bind cluster flags
if f.APIServer != nil {
overrides.ClusterInfo.Server = *f.APIServer
}
if f.CAFile != nil {
overrides.ClusterInfo.CertificateAuthority = *f.CAFile
}
if f.Insecure != nil {
overrides.ClusterInfo.InsecureSkipTLSVerify = *f.Insecure
}
// bind context flags
if f.Context != nil {
overrides.CurrentContext = *f.Context
}
if f.ClusterName != nil {
overrides.Context.Cluster = *f.ClusterName
}
if f.AuthInfoName != nil {
overrides.Context.AuthInfo = *f.AuthInfoName
}
if f.Namespace != nil {
overrides.Context.Namespace = *f.Namespace
}
if f.Timeout != nil {
overrides.Timeout = *f.Timeout
}
var clientConfig clientcmd.ClientConfig
// we only have an interactive prompt when a password is allowed
if f.Password == nil {
clientConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
} else {
clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
}
return clientConfig
}
// ToDiscoveryClient returns a CachedDiscoveryInterface using a computed RESTConfig.
// ToDiscoveryClient implements RESTClientGetter.
// Expects the AddFlags method to have been called.
// Returns a CachedDiscoveryInterface using a computed RESTConfig.
func (f *ConfigFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
config, err := f.ToRESTConfig()
if err != nil {
@@ -71,7 +175,7 @@ func (f *ConfigFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, e
// double it just so we don't end up here again for a while. This config is only used for discovery.
config.Burst = 100
cacheDir := ""
cacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
if f.CacheDir != nil {
cacheDir = *f.CacheDir
}
@@ -94,28 +198,135 @@ func (f *ConfigFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, e
return NewCachedDiscoveryClient(discoveryClient, cacheDir, time.Duration(10*time.Minute)), nil
}
func (f *ConfigFlags) AddFlags(cmd *cobra.Command) {
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
// short flagnames are disabled by default. These are here for compatibility with existing scripts
flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
clientcmd.BindOverrideFlags(overrides, cmd.Flags(), flagNames)
// Normalize all flags that are coming from other packages or pre-configurations
// a.k.a. change all "_" to "-". e.g. glog package
flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
if f.KubeConfig != nil {
cmd.Flags().StringVar(f.KubeConfig, "kubeconfig", *f.KubeConfig, "Path to the kubeconfig file to use for CLI requests.")
flags.StringVar(f.KubeConfig, "kubeconfig", *f.KubeConfig, "Path to the kubeconfig file to use for CLI requests.")
}
if f.CacheDir != nil {
cmd.Flags().StringVar(f.CacheDir, FlagHTTPCacheDir, *f.CacheDir, "Default HTTP cache directory")
flags.StringVar(f.CacheDir, FlagHTTPCacheDir, *f.CacheDir, "Default HTTP cache directory")
}
// add config options
if f.CertFile != nil {
flags.StringVar(f.CertFile, flagCertFile, *f.CertFile, "Path to a client certificate file for TLS")
}
if f.KeyFile != nil {
flags.StringVar(f.KeyFile, flagKeyFile, *f.KeyFile, "Path to a client key file for TLS")
}
if f.BearerToken != nil {
flags.StringVar(f.BearerToken, flagBearerToken, *f.BearerToken, "Bearer token for authentication to the API server")
}
if f.Impersonate != nil {
flags.StringVar(f.Impersonate, flagImpersonate, *f.Impersonate, "Username to impersonate for the operation")
}
if f.ImpersonateGroup != nil {
flags.StringArrayVar(f.ImpersonateGroup, flagImpersonateGroup, *f.ImpersonateGroup, "Group to impersonate for the operation, this flag can be repeated to specify multiple groups.")
}
if f.Username != nil {
flags.StringVar(f.Username, flagUsername, *f.Username, "Username for basic authentication to the API server")
}
if f.Password != nil {
flags.StringVar(f.Password, flagPassword, *f.Password, "Password for basic authentication to the API server")
}
if f.ClusterName != nil {
flags.StringVar(f.ClusterName, flagClusterName, *f.ClusterName, "The name of the kubeconfig cluster to use")
}
if f.AuthInfoName != nil {
flags.StringVar(f.AuthInfoName, flagAuthInfoName, *f.AuthInfoName, "The name of the kubeconfig user to use")
}
if f.Namespace != nil {
flags.StringVarP(f.Namespace, flagNamespace, "n", *f.Namespace, "If present, the namespace scope for this CLI request")
}
if f.Context != nil {
flags.StringVar(f.Context, flagContext, *f.Context, "The name of the kubeconfig context to use")
}
if f.APIServer != nil {
flags.StringVarP(f.APIServer, flagAPIServer, "s", *f.APIServer, "The address and port of the Kubernetes API server")
}
if f.Insecure != nil {
flags.BoolVar(f.Insecure, flagInsecure, *f.Insecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
}
if f.CAFile != nil {
flags.StringVar(f.CAFile, flagCAFile, *f.CAFile, "Path to a cert file for the certificate authority")
}
if f.Timeout != nil {
flags.StringVar(f.Timeout, flagTimeout, *f.Timeout, "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests.")
}
}
func (f *ConfigFlags) WithDeprecatedPasswordFlag() *ConfigFlags {
f.Username = stringptr("")
f.Password = stringptr("")
return f
}
func NewConfigFlags() *ConfigFlags {
defaultCacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
kubeConfig := ""
impersonateGroup := []string{}
insecure := false
return &ConfigFlags{
CacheDir: &defaultCacheDir,
KubeConfig: &kubeConfig,
Insecure: &insecure,
Timeout: stringptr("0"),
KubeConfig: stringptr(""),
ClusterName: stringptr(""),
AuthInfoName: stringptr(""),
Context: stringptr(""),
Namespace: stringptr(""),
APIServer: stringptr(""),
CertFile: stringptr(""),
KeyFile: stringptr(""),
CAFile: stringptr(""),
BearerToken: stringptr(""),
Impersonate: stringptr(""),
ImpersonateGroup: &impersonateGroup,
}
}
func stringptr(val string) *string {
return &val
}
// TODO(juanvallejo): move to separate file when config_flags are moved
// to pkg/kubectl/genericclioptions
type TestConfigFlags struct {
clientConfig clientcmd.ClientConfig
discoveryClient discovery.CachedDiscoveryInterface
}
func (f *TestConfigFlags) ToRawKubeConfigLoader() clientcmd.ClientConfig {
if f.clientConfig == nil {
panic("attempt to obtain a test RawKubeConfigLoader with no clientConfig specified")
}
return f.clientConfig
}
func (f *TestConfigFlags) ToRESTConfig() (*rest.Config, error) {
return f.ToRawKubeConfigLoader().ClientConfig()
}
func (f *TestConfigFlags) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return f.discoveryClient, nil
}
func (f *TestConfigFlags) WithClientConfig(clientConfig clientcmd.ClientConfig) *TestConfigFlags {
f.clientConfig = clientConfig
return f
}
func (f *TestConfigFlags) WithDiscoveryClient(c discovery.CachedDiscoveryInterface) *TestConfigFlags {
f.discoveryClient = c
return f
}
func NewTestConfigFlags() *TestConfigFlags {
return &TestConfigFlags{}
}

View File

@@ -37,7 +37,6 @@ import (
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
scaleclient "k8s.io/client-go/scale"
"k8s.io/client-go/tools/clientcmd"
api "k8s.io/kubernetes/pkg/apis/core"
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
@@ -125,10 +124,9 @@ type ClientAccessFactory interface {
// Command will stringify and return all environment arguments ie. a command run by a client
// using the factory.
Command(cmd *cobra.Command, showSecrets bool) string
// BindFlags adds any flags that are common to all kubectl sub commands.
BindFlags(flags *pflag.FlagSet)
// BindExternalFlags adds any flags defined by external projects (not part of pflags)
BindExternalFlags(flags *pflag.FlagSet)
// SuggestedPodTemplateResources returns a list of resource types that declare a pod template
SuggestedPodTemplateResources() []schema.GroupResource
@@ -227,10 +225,9 @@ type factory struct {
}
// NewFactory creates a factory with the default Kubernetes resources defined
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
// if optionalClientConfig is not nil, then this factory will make use of it.
func NewFactory(optionalClientConfig clientcmd.ClientConfig) Factory {
clientAccessFactory := NewClientAccessFactory(optionalClientConfig)
// Receives a clientGetter capable of providing a discovery client and a REST client configuration.
func NewFactory(clientGetter RESTClientGetter) Factory {
clientAccessFactory := NewClientAccessFactory(clientGetter)
objectMappingFactory := NewObjectMappingFactory(clientAccessFactory)
builderFactory := NewBuilderFactory(clientAccessFactory, objectMappingFactory)

View File

@@ -20,23 +20,19 @@ package util
import (
"errors"
"flag"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"sync"
"k8s.io/api/core/v1"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"sync"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
@@ -50,13 +46,11 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core"
@@ -64,124 +58,37 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/transport"
"k8s.io/kubernetes/pkg/version"
)
type RESTClientGetter interface {
ToRESTConfig() (*restclient.Config, error)
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
ToRawKubeConfigLoader() clientcmd.ClientConfig
}
type ring0Factory struct {
flags *pflag.FlagSet
clientConfig clientcmd.ClientConfig
discoveryCacheDir string
clientGetter RESTClientGetter
requireMatchedServerVersion bool
checkServerVersion sync.Once
matchesServerVersionErr error
}
func NewClientAccessFactory(optionalClientConfig clientcmd.ClientConfig) ClientAccessFactory {
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
clientConfig := optionalClientConfig
if optionalClientConfig == nil {
clientConfig = DefaultClientConfig(flags)
func NewClientAccessFactory(clientGetter RESTClientGetter) ClientAccessFactory {
if clientGetter == nil {
panic("attempt to instantiate client_access_factory with nil clientGetter")
}
flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
f := &ring0Factory{
flags: flags,
clientConfig: clientConfig,
clientGetter: clientGetter,
}
return f
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge the kubeconfig itself. This is done with the following hierarchy rules:
// 1. CommandLineLocation - this parsed from the command line, so it must be late bound. If you specify this,
// then no other kubeconfig files are merged. This file must exist.
// 2. If $KUBECONFIG is set, then it is treated as a list of files that should be merged.
// 3. HomeDirectoryLocation
// Empty filenames are ignored. Files with non-deserializable content produced errors.
// The first file to set a particular value or map key wins and the value or map key is never changed.
// This means that the first file to set CurrentContext will have its context preserved. It also means
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
// non-conflicting entries from the second file's "red-user" are discarded.
// 2. Determine the context to use based on the first hit in this chain
// 1. command line argument - again, parsed from the command line, so it must be late bound
// 2. CurrentContext from the merged kubeconfig file
// 3. Empty is allowed at this stage
// 3. Determine the cluster info and auth info to use. At this point, we may or may not have a context. They
// are built based on the first hit in this chain. (run it twice, once for auth, once for cluster)
// 1. command line argument
// 2. If context is present, then use the context value
// 3. Empty is allowed
// 4. Determine the actual cluster info to use. At this point, we may or may not have a cluster info. Build
// each piece of the cluster info based on the chain:
// 1. command line argument
// 2. If cluster info is present and a value for the attribute is present, use it.
// 3. If you don't have a server location, bail.
// 5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
// technique per auth info. The following conditions result in an error:
// 1. If there are two conflicting techniques specified from the command line, fail.
// 2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
// 3. If the command line specifies one and the auth info specifies another, honor the command line technique.
// 2. Use default values and potentially prompt for auth information
//
// However, if it appears that we're running in a kubernetes cluster
// container environment, then run with the auth info kubernetes mounted for
// us. Specifically:
// The env vars KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT are
// set, and the file /var/run/secrets/kubernetes.io/serviceaccount/token
// exists and is not a directory.
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// use the standard defaults for this client command
// DEPRECATED: remove and replace with something more accurate
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
// short flagnames are disabled by default. These are here for compatibility with existing scripts
flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
return clientConfig
}
func (f *ring0Factory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
cfg, err := f.clientConfig.ClientConfig()
if err != nil {
return nil, err
}
// The more groups you have, the more discovery requests you need to make.
// given 25 groups (our groups + a few custom resources) with one-ish version each, discovery needs to make 50 requests
// double it just so we don't end up here again for a while. This config is only used for discovery.
cfg.Burst = 100
if f.discoveryCacheDir != "" {
wt := cfg.WrapTransport
cfg.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
if wt != nil {
rt = wt(rt)
}
return transport.NewCacheRoundTripper(f.discoveryCacheDir, rt)
}
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
return nil, err
}
cacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube", "cache", "discovery"), cfg.Host)
return NewCachedDiscoveryClient(discoveryClient, cacheDir, time.Duration(10*time.Minute)), nil
return f.clientGetter.ToDiscoveryClient()
}
func (f *ring0Factory) KubernetesClientSet() (*kubernetes.Clientset, error) {
@@ -227,7 +134,7 @@ func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
if err := f.checkMatchingServerVersion(); err != nil {
return nil, err
}
clientConfig, err := f.clientConfig.ClientConfig()
clientConfig, err := f.clientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
@@ -235,7 +142,7 @@ func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
return clientConfig, nil
}
func (f *ring0Factory) BareClientConfig() (*restclient.Config, error) {
return f.clientConfig.ClientConfig()
return f.clientGetter.ToRESTConfig()
}
func (f *ring0Factory) RESTClient() (*restclient.RESTClient, error) {
@@ -413,26 +320,11 @@ func (f *ring0Factory) Command(cmd *cobra.Command, showSecrets bool) string {
}
func (f *ring0Factory) BindFlags(flags *pflag.FlagSet) {
// Merge factory's flags
flags.AddFlagSet(f.flags)
// Globally persistent flags across all subcommands.
// TODO Change flag names to consts to allow safer lookup from subcommands.
// TODO Add a verbose flag that turns on glog logging. Probably need a way
// to do that automatically for every subcommand.
flags.BoolVar(&f.requireMatchedServerVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
defaultCacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
flags.StringVar(&f.discoveryCacheDir, FlagHTTPCacheDir, defaultCacheDir, "Default HTTP cache directory")
// Normalize all flags that are coming from other packages or pre-configurations
// a.k.a. change all "_" to "-". e.g. glog package
flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
}
func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) {
// any flags defined by external projects (not part of pflags)
flags.AddGoFlagSet(flag.CommandLine)
}
func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource {
@@ -476,7 +368,7 @@ func (f *ring0Factory) Resumer(info *resource.Info) ([]byte, error) {
}
func (f *ring0Factory) DefaultNamespace() (string, bool, error) {
return f.clientConfig.Namespace()
return f.clientGetter.ToRawKubeConfigLoader().Namespace()
}
const (

View File

@@ -44,7 +44,7 @@ import (
)
func TestPortsForObject(t *testing.T) {
f := NewFactory(nil)
f := NewFactory(NewTestConfigFlags())
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
@@ -75,7 +75,7 @@ func TestPortsForObject(t *testing.T) {
}
func TestProtocolsForObject(t *testing.T) {
f := NewFactory(nil)
f := NewFactory(NewTestConfigFlags())
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
@@ -113,7 +113,7 @@ func TestProtocolsForObject(t *testing.T) {
}
func TestLabelsForObject(t *testing.T) {
f := NewFactory(nil)
f := NewFactory(NewTestConfigFlags())
tests := []struct {
name string
@@ -164,7 +164,7 @@ func TestLabelsForObject(t *testing.T) {
}
func TestCanBeExposed(t *testing.T) {
factory := NewFactory(nil)
factory := NewFactory(NewTestConfigFlags())
tests := []struct {
kind schema.GroupKind
expectErr bool

View File

@@ -0,0 +1,33 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["round_tripper.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/transport",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/gregjones/httpcache:go_default_library",
"//vendor/github.com/gregjones/httpcache/diskcache:go_default_library",
"//vendor/github.com/peterbourgon/diskv:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["round_tripper_test.go"],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,51 @@
/*
Copyright 2017 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 transport provides a round tripper capable of caching HTTP responses.
package transport
import (
"net/http"
"path/filepath"
"github.com/gregjones/httpcache"
"github.com/gregjones/httpcache/diskcache"
"github.com/peterbourgon/diskv"
)
type cacheRoundTripper struct {
rt *httpcache.Transport
}
// NewCacheRoundTripper creates a roundtripper that reads the ETag on
// response headers and send the If-None-Match header on subsequent
// corresponding requests.
func NewCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper {
d := diskv.New(diskv.Options{
BasePath: cacheDir,
TempDir: filepath.Join(cacheDir, ".diskv-temp"),
})
t := httpcache.NewTransport(diskcache.NewWithDiskv(d))
t.Transport = rt
return &cacheRoundTripper{rt: t}
}
func (rt *cacheRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return rt.rt.RoundTrip(req)
}
func (rt *cacheRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt.Transport }

View File

@@ -0,0 +1,95 @@
/*
Copyright 2017 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 transport
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
"os"
"testing"
)
// copied from k8s.io/client-go/transport/round_trippers_test.go
type testRoundTripper struct {
Request *http.Request
Response *http.Response
Err error
}
func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
rt.Request = req
return rt.Response, rt.Err
}
func TestCacheRoundTripper(t *testing.T) {
rt := &testRoundTripper{}
cacheDir, err := ioutil.TempDir("", "cache-rt")
defer os.RemoveAll(cacheDir)
if err != nil {
t.Fatal(err)
}
cache := NewCacheRoundTripper(cacheDir, rt)
// First call, caches the response
req := &http.Request{
Method: http.MethodGet,
URL: &url.URL{Host: "localhost"},
}
rt.Response = &http.Response{
Header: http.Header{"ETag": []string{`"123456"`}},
Body: ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
StatusCode: http.StatusOK,
}
resp, err := cache.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if string(content) != "Content" {
t.Errorf(`Expected Body to be "Content", got %q`, string(content))
}
// Second call, returns cached response
req = &http.Request{
Method: http.MethodGet,
URL: &url.URL{Host: "localhost"},
}
rt.Response = &http.Response{
StatusCode: http.StatusNotModified,
Body: ioutil.NopCloser(bytes.NewReader([]byte("Other Content"))),
}
resp, err = cache.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
// Read body and make sure we have the initial content
content, err = ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
t.Fatal(err)
}
if string(content) != "Content" {
t.Errorf("Invalid content read from cache %q", string(content))
}
}