Merge pull request #33136 from smarterclayton/default_config

Automatic merge from submit-queue

When client config is default or default is invalid, check ICC

Alternative fix to #33019
This commit is contained in:
Kubernetes Submit Queue 2016-09-21 02:21:34 -07:00 committed by GitHub
commit 4099a5cc98
6 changed files with 86 additions and 47 deletions

View File

@ -34,16 +34,25 @@ import (
) )
var ( var (
// DefaultCluster is the cluster config used when no other config is specified // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
// TODO: eventually apiserver should start on 443 and be secure by default // DEPRECATED will be replaced
DefaultCluster = clientcmdapi.Cluster{Server: "http://localhost:8080"} ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
// DefaultClientConfig represents the legacy behavior of this package for defaulting
// EnvVarCluster allows overriding the DefaultCluster using an envvar for the server name // DEPRECATED will be replace
EnvVarCluster = clientcmdapi.Cluster{Server: os.Getenv("KUBERNETES_MASTER")} DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
ClusterDefaults: ClusterDefaults,
DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{}, nil, NewDefaultClientConfigLoadingRules()} }, nil, NewDefaultClientConfigLoadingRules()}
) )
// getDefaultServer returns a default setting for DefaultClientConfig
// DEPRECATED
func getDefaultServer() string {
if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 {
return server
}
return "http://localhost:8080"
}
// ClientConfig is used to make it easy to get an api server client // ClientConfig is used to make it easy to get an api server client
type ClientConfig interface { type ClientConfig interface {
// RawConfig returns the merged result of all overrides // RawConfig returns the merged result of all overrides
@ -347,7 +356,6 @@ func (config *DirectClientConfig) getCluster() clientcmdapi.Cluster {
var mergedClusterInfo clientcmdapi.Cluster var mergedClusterInfo clientcmdapi.Cluster
mergo.Merge(&mergedClusterInfo, config.overrides.ClusterDefaults) mergo.Merge(&mergedClusterInfo, config.overrides.ClusterDefaults)
mergo.Merge(&mergedClusterInfo, EnvVarCluster)
if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists { if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
mergo.Merge(&mergedClusterInfo, configClusterInfo) mergo.Merge(&mergedClusterInfo, configClusterInfo)
} }

View File

@ -293,8 +293,6 @@ func TestCreateCleanWithPrefix(t *testing.T) {
{"anything", "anything"}, {"anything", "anything"},
} }
// WARNING: EnvVarCluster.Server is set during package loading time and can not be overridden by os.Setenv inside this test
EnvVarCluster.Server = ""
tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"}) tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"})
for _, tc := range tt { for _, tc := range tt {
@ -305,7 +303,7 @@ func TestCreateCleanWithPrefix(t *testing.T) {
config.Clusters["clean"] = cleanConfig config.Clusters["clean"] = cleanConfig
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{ clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
ClusterDefaults: DefaultCluster, ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
}, nil) }, nil)
clientConfig, err := clientBuilder.ClientConfig() clientConfig, err := clientBuilder.ClientConfig()
@ -334,7 +332,7 @@ func TestCreateCleanDefault(t *testing.T) {
func TestCreateCleanDefaultCluster(t *testing.T) { func TestCreateCleanDefaultCluster(t *testing.T) {
config := createValidTestConfig() config := createValidTestConfig()
clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{ clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{
ClusterDefaults: DefaultCluster, ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
}) })
clientConfig, err := clientBuilder.ClientConfig() clientConfig, err := clientBuilder.ClientConfig()
@ -362,7 +360,7 @@ func TestCreateMissingContext(t *testing.T) {
const expectedErrorContains = "context was not found for specified context: not-present" const expectedErrorContains = "context was not found for specified context: not-present"
config := createValidTestConfig() config := createValidTestConfig()
clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{ clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{
ClusterDefaults: DefaultCluster, ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
}, nil) }, nil)
_, err := clientBuilder.ClientConfig() _, err := clientBuilder.ClientConfig()

View File

@ -23,6 +23,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
goruntime "runtime" goruntime "runtime"
"strings" "strings"
@ -30,6 +31,7 @@ import (
"github.com/imdario/mergo" "github.com/imdario/mergo"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
@ -65,6 +67,9 @@ func currentMigrationRules() map[string]string {
type ClientConfigLoader interface { type ClientConfigLoader interface {
ConfigAccess ConfigAccess
// IsDefaultConfig returns true if the returned config matches the defaults.
IsDefaultConfig(*restclient.Config) bool
// Load returns the latest config
Load() (*clientcmdapi.Config, error) Load() (*clientcmdapi.Config, error)
} }
@ -96,6 +101,9 @@ func (g *ClientConfigGetter) IsExplicitFile() bool {
func (g *ClientConfigGetter) GetExplicitFile() string { func (g *ClientConfigGetter) GetExplicitFile() string {
return "" return ""
} }
func (g *ClientConfigGetter) IsDefaultConfig(config *restclient.Config) bool {
return false
}
// ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config // ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config
// Callers can put the chain together however they want, but we'd recommend: // Callers can put the chain together however they want, but we'd recommend:
@ -112,6 +120,10 @@ type ClientConfigLoadingRules struct {
// DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files. This is phrased as a negative so // DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files. This is phrased as a negative so
// that a default object that doesn't set this will usually get the behavior it wants. // that a default object that doesn't set this will usually get the behavior it wants.
DoNotResolvePaths bool DoNotResolvePaths bool
// DefaultClientConfig is an optional field indicating what rules to use to calculate a default configuration.
// This should match the overrides passed in to ClientConfig loader.
DefaultClientConfig ClientConfig
} }
// ClientConfigLoadingRules implements the ClientConfigLoader interface. // ClientConfigLoadingRules implements the ClientConfigLoader interface.
@ -192,6 +204,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
// first merge all of our maps // first merge all of our maps
mapConfig := clientcmdapi.NewConfig() mapConfig := clientcmdapi.NewConfig()
for _, kubeconfig := range kubeconfigs { for _, kubeconfig := range kubeconfigs {
mergo.Merge(mapConfig, kubeconfig) mergo.Merge(mapConfig, kubeconfig)
} }
@ -316,6 +329,18 @@ func (rules *ClientConfigLoadingRules) GetExplicitFile() string {
return rules.ExplicitPath return rules.ExplicitPath
} }
// IsDefaultConfig returns true if the provided configuration matches the default
func (rules *ClientConfigLoadingRules) IsDefaultConfig(config *restclient.Config) bool {
if rules.DefaultClientConfig == nil {
return false
}
defaultConfig, err := rules.DefaultClientConfig.ClientConfig()
if err != nil {
return false
}
return reflect.DeepEqual(config, defaultConfig)
}
// LoadFromFile takes a filename and deserializes the contents into Config object // LoadFromFile takes a filename and deserializes the contents into Config object
func LoadFromFile(filename string) (*clientcmdapi.Config, error) { func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
kubeconfigBytes, err := ioutil.ReadFile(filename) kubeconfigBytes, err := ioutil.ReadFile(filename)

View File

@ -18,7 +18,6 @@ package clientcmd
import ( import (
"io" "io"
"reflect"
"sync" "sync"
"github.com/golang/glog" "github.com/golang/glog"
@ -105,20 +104,15 @@ func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, e
// content differs from the default config // content differs from the default config
mergedConfig, err := mergedClientConfig.ClientConfig() mergedConfig, err := mergedClientConfig.ClientConfig()
switch { switch {
case err != nil && !IsEmptyConfig(err): case err != nil:
// return on any error except empty config if !IsEmptyConfig(err) {
return nil, err // return on any error except empty config
case mergedConfig != nil: return nil, err
// if the configuration has any settings at all, we cannot use ICC
// TODO: we need to discriminate better between "empty due to env" and
// "empty due to defaults"
// TODO: this shouldn't be a global - the client config rules should be
// handling this.
defaultConfig, defErr := DefaultClientConfig.ClientConfig()
if IsConfigurationInvalid(defErr) && !IsEmptyConfig(err) {
return mergedConfig, nil
} }
if defErr == nil && !reflect.DeepEqual(mergedConfig, defaultConfig) { case mergedConfig != nil:
// the configuration is valid, but if this is equal to the defaults we should try
// in-cluster configuration
if !config.loader.IsDefaultConfig(mergedConfig) {
return mergedConfig, nil return mergedConfig, nil
} }
} }

View File

@ -70,20 +70,27 @@ func (icc *testICC) Possible() bool {
} }
func TestInClusterConfig(t *testing.T) { func TestInClusterConfig(t *testing.T) {
// override direct client config for this run
originalDefault := DefaultClientConfig
defer func() {
DefaultClientConfig = originalDefault
}()
baseDefault := &DirectClientConfig{
overrides: &ConfigOverrides{},
}
default1 := &DirectClientConfig{ default1 := &DirectClientConfig{
config: *createValidTestConfig(), config: *createValidTestConfig(),
contextName: "clean", contextName: "clean",
overrides: &ConfigOverrides{}, overrides: &ConfigOverrides{},
} }
invalidDefaultConfig := clientcmdapi.NewConfig()
invalidDefaultConfig.Clusters["clean"] = &clientcmdapi.Cluster{
Server: "http://localhost:8080",
}
invalidDefaultConfig.Contexts["other"] = &clientcmdapi.Context{
Cluster: "clean",
}
invalidDefaultConfig.CurrentContext = "clean"
defaultInvalid := &DirectClientConfig{
config: *invalidDefaultConfig,
overrides: &ConfigOverrides{},
}
if _, err := defaultInvalid.ClientConfig(); err == nil || !IsConfigurationInvalid(err) {
t.Fatal(err)
}
config1, err := default1.ClientConfig() config1, err := default1.ClientConfig()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -128,6 +135,16 @@ func TestInClusterConfig(t *testing.T) {
err: nil, err: nil,
}, },
"in-cluster not checked when default config is invalid": {
defaultConfig: defaultInvalid,
clientConfig: &testClientConfig{config: config1},
icc: &testICC{},
checkedICC: false,
result: config1,
err: nil,
},
"in-cluster not checked when config is not equal to default": { "in-cluster not checked when config is not equal to default": {
defaultConfig: default1, defaultConfig: default1,
clientConfig: &testClientConfig{config: config2}, clientConfig: &testClientConfig{config: config2},
@ -175,7 +192,7 @@ func TestInClusterConfig(t *testing.T) {
err: nil, err: nil,
}, },
"in-cluster not checked when default is invalid": { "in-cluster not checked when standard default is invalid": {
defaultConfig: &DefaultClientConfig, defaultConfig: &DefaultClientConfig,
clientConfig: &testClientConfig{config: config2}, clientConfig: &testClientConfig{config: config2},
icc: &testICC{}, icc: &testICC{},
@ -187,12 +204,8 @@ func TestInClusterConfig(t *testing.T) {
} }
for name, test := range testCases { for name, test := range testCases {
if test.defaultConfig != nil {
DefaultClientConfig = *test.defaultConfig
} else {
DefaultClientConfig = *baseDefault
}
c := &DeferredLoadingClientConfig{icc: test.icc} c := &DeferredLoadingClientConfig{icc: test.icc}
c.loader = &ClientConfigLoadingRules{DefaultClientConfig: test.defaultConfig}
c.clientConfig = test.clientConfig c.clientConfig = test.clientConfig
cfg, err := c.ClientConfig() cfg, err := c.ClientConfig()

View File

@ -34,7 +34,6 @@ import (
"github.com/blang/semver" "github.com/blang/semver"
"github.com/emicklei/go-restful/swagger" "github.com/emicklei/go-restful/swagger"
"github.com/imdario/mergo"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -1161,11 +1160,13 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
// exists and is not a directory. // exists and is not a directory.
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 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.") flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &clientcmd.ConfigOverrides{} overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
// use the standard defaults for this client config
mergo.Merge(&overrides.ClusterDefaults, clientcmd.DefaultCluster)
flagNames := clientcmd.RecommendedConfigOverrideFlags("") flagNames := clientcmd.RecommendedConfigOverrideFlags("")
// short flagnames are disabled by default. These are here for compatibility with existing scripts // short flagnames are disabled by default. These are here for compatibility with existing scripts