From 8c326993d5ace6cc8d06ee28d0dc391860bfd5ed Mon Sep 17 00:00:00 2001 From: deads2k Date: Mon, 15 Feb 2016 10:42:38 -0500 Subject: [PATCH] only load kubeconfig files one time --- .../unversioned/clientcmd/client_config.go | 26 +++++----- pkg/client/unversioned/clientcmd/loader.go | 51 +++++++++---------- .../clientcmd/merged_client_builder.go | 47 ++++++++++------- 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/pkg/client/unversioned/clientcmd/client_config.go b/pkg/client/unversioned/clientcmd/client_config.go index ce94a128bb0..e5af51b32c0 100644 --- a/pkg/client/unversioned/clientcmd/client_config.go +++ b/pkg/client/unversioned/clientcmd/client_config.go @@ -67,25 +67,25 @@ type DirectClientConfig struct { // NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig { - return DirectClientConfig{config, config.CurrentContext, overrides, nil} + return &DirectClientConfig{config, config.CurrentContext, overrides, nil} } // NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides) ClientConfig { - return DirectClientConfig{config, contextName, overrides, nil} + return &DirectClientConfig{config, contextName, overrides, nil} } // NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { - return DirectClientConfig{config, contextName, overrides, fallbackReader} + return &DirectClientConfig{config, contextName, overrides, fallbackReader} } -func (config DirectClientConfig) RawConfig() (clientcmdapi.Config, error) { +func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) { return config.config, nil } // ClientConfig implements ClientConfig -func (config DirectClientConfig) ClientConfig() (*client.Config, error) { +func (config *DirectClientConfig) ClientConfig() (*client.Config, error) { if err := config.ConfirmUsable(); err != nil { return nil, err } @@ -217,7 +217,7 @@ func canIdentifyUser(config client.Config) bool { } // Namespace implements KubeConfig -func (config DirectClientConfig) Namespace() (string, bool, error) { +func (config *DirectClientConfig) Namespace() (string, bool, error) { if err := config.ConfirmUsable(); err != nil { return "", false, err } @@ -237,7 +237,7 @@ func (config DirectClientConfig) Namespace() (string, bool, error) { // ConfirmUsable looks a particular context and determines if that particular part of the config is useable. There might still be errors in the config, // but no errors in the sections requested or referenced. It does not return early so that it can find as many errors as possible. -func (config DirectClientConfig) ConfirmUsable() error { +func (config *DirectClientConfig) ConfirmUsable() error { validationErrors := make([]error, 0) validationErrors = append(validationErrors, validateAuthInfo(config.getAuthInfoName(), config.getAuthInfo())...) validationErrors = append(validationErrors, validateClusterInfo(config.getClusterName(), config.getCluster())...) @@ -249,7 +249,7 @@ func (config DirectClientConfig) ConfirmUsable() error { return newErrConfigurationInvalid(validationErrors) } -func (config DirectClientConfig) getContextName() string { +func (config *DirectClientConfig) getContextName() string { if len(config.overrides.CurrentContext) != 0 { return config.overrides.CurrentContext } @@ -260,21 +260,21 @@ func (config DirectClientConfig) getContextName() string { return config.config.CurrentContext } -func (config DirectClientConfig) getAuthInfoName() string { +func (config *DirectClientConfig) getAuthInfoName() string { if len(config.overrides.Context.AuthInfo) != 0 { return config.overrides.Context.AuthInfo } return config.getContext().AuthInfo } -func (config DirectClientConfig) getClusterName() string { +func (config *DirectClientConfig) getClusterName() string { if len(config.overrides.Context.Cluster) != 0 { return config.overrides.Context.Cluster } return config.getContext().Cluster } -func (config DirectClientConfig) getContext() clientcmdapi.Context { +func (config *DirectClientConfig) getContext() clientcmdapi.Context { contexts := config.config.Contexts contextName := config.getContextName() @@ -287,7 +287,7 @@ func (config DirectClientConfig) getContext() clientcmdapi.Context { return mergedContext } -func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { +func (config *DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { authInfos := config.config.AuthInfos authInfoName := config.getAuthInfoName() @@ -300,7 +300,7 @@ func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { return mergedAuthInfo } -func (config DirectClientConfig) getCluster() clientcmdapi.Cluster { +func (config *DirectClientConfig) getCluster() clientcmdapi.Cluster { clusterInfos := config.config.Clusters clusterInfoName := config.getClusterName() diff --git a/pkg/client/unversioned/clientcmd/loader.go b/pkg/client/unversioned/clientcmd/loader.go index 215431583da..065559a9c01 100644 --- a/pkg/client/unversioned/clientcmd/loader.go +++ b/pkg/client/unversioned/clientcmd/loader.go @@ -117,23 +117,41 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { } else { kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) + } + kubeconfigs := []*clientcmdapi.Config{} + // read and cache the config files so that we only look at them once + for _, filename := range kubeConfigFiles { + if len(filename) == 0 { + // no work to do + continue + } + + config, err := LoadFromFile(filename) + if os.IsNotExist(err) { + // skip missing files + continue + } + if err != nil { + errlist = append(errlist, fmt.Errorf("Error loading config file \"%s\": %v", filename, err)) + continue + } + + kubeconfigs = append(kubeconfigs, config) } // first merge all of our maps mapConfig := clientcmdapi.NewConfig() - for _, file := range kubeConfigFiles { - if err := mergeConfigWithFile(mapConfig, file); err != nil { - errlist = append(errlist, err) - } + for _, kubeconfig := range kubeconfigs { + mergo.Merge(mapConfig, kubeconfig) } // merge all of the struct values in the reverse order so that priority is given correctly // errors are not added to the list the second time nonMapConfig := clientcmdapi.NewConfig() - for i := len(kubeConfigFiles) - 1; i >= 0; i-- { - file := kubeConfigFiles[i] - mergeConfigWithFile(nonMapConfig, file) + for i := len(kubeconfigs) - 1; i >= 0; i-- { + kubeconfig := kubeconfigs[i] + mergo.Merge(nonMapConfig, kubeconfig) } // since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and @@ -198,25 +216,6 @@ func (rules *ClientConfigLoadingRules) Migrate() error { return nil } -func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error { - if len(filename) == 0 { - // no work to do - return nil - } - - config, err := LoadFromFile(filename) - if os.IsNotExist(err) { - return nil - } - if err != nil { - return fmt.Errorf("Error loading config file \"%s\": %v", filename, err) - } - - mergo.Merge(startingConfig, config) - - return nil -} - // LoadFromFile takes a filename and deserializes the contents into Config object func LoadFromFile(filename string) (*clientcmdapi.Config, error) { kubeconfigBytes, err := ioutil.ReadFile(filename) diff --git a/pkg/client/unversioned/clientcmd/merged_client_builder.go b/pkg/client/unversioned/clientcmd/merged_client_builder.go index 2888981f9f7..8fee4b9ece7 100644 --- a/pkg/client/unversioned/clientcmd/merged_client_builder.go +++ b/pkg/client/unversioned/clientcmd/merged_client_builder.go @@ -19,6 +19,7 @@ package clientcmd import ( "io" "reflect" + "sync" "github.com/golang/glog" @@ -35,35 +36,47 @@ type DeferredLoadingClientConfig struct { loadingRules *ClientConfigLoadingRules overrides *ConfigOverrides fallbackReader io.Reader + + clientConfig ClientConfig + loadingLock sync.Mutex } // NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name func NewNonInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides) ClientConfig { - return DeferredLoadingClientConfig{loadingRules, overrides, nil} + return &DeferredLoadingClientConfig{loadingRules: loadingRules, overrides: overrides} } // NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader func NewInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { - return DeferredLoadingClientConfig{loadingRules, overrides, fallbackReader} + return &DeferredLoadingClientConfig{loadingRules: loadingRules, overrides: overrides, fallbackReader: fallbackReader} } -func (config DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) { - mergedConfig, err := config.loadingRules.Load() - if err != nil { - return nil, err +func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) { + if config.clientConfig == nil { + config.loadingLock.Lock() + defer config.loadingLock.Unlock() + + if config.clientConfig == nil { + mergedConfig, err := config.loadingRules.Load() + if err != nil { + return nil, err + } + + var mergedClientConfig ClientConfig + if config.fallbackReader != nil { + mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader) + } else { + mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides) + } + + config.clientConfig = mergedClientConfig + } } - var mergedClientConfig ClientConfig - if config.fallbackReader != nil { - mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader) - } else { - mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides) - } - - return mergedClientConfig, nil + return config.clientConfig, nil } -func (config DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { +func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { mergedConfig, err := config.createClientConfig() if err != nil { return clientcmdapi.Config{}, err @@ -73,7 +86,7 @@ func (config DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, erro } // ClientConfig implements ClientConfig -func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) { +func (config *DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) { mergedClientConfig, err := config.createClientConfig() if err != nil { return nil, err @@ -94,7 +107,7 @@ func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) } // Namespace implements KubeConfig -func (config DeferredLoadingClientConfig) Namespace() (string, bool, error) { +func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) { mergedKubeConfig, err := config.createClientConfig() if err != nil { return "", false, err