only load kubeconfig files one time

This commit is contained in:
deads2k 2016-02-15 10:42:38 -05:00
parent ef505d8fa3
commit 8c326993d5
3 changed files with 68 additions and 56 deletions

View File

@ -67,25 +67,25 @@ type DirectClientConfig struct {
// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name // NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig { 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 // 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 { 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 // 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 { 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 return config.config, nil
} }
// ClientConfig implements ClientConfig // ClientConfig implements ClientConfig
func (config DirectClientConfig) ClientConfig() (*client.Config, error) { func (config *DirectClientConfig) ClientConfig() (*client.Config, error) {
if err := config.ConfirmUsable(); err != nil { if err := config.ConfirmUsable(); err != nil {
return nil, err return nil, err
} }
@ -217,7 +217,7 @@ func canIdentifyUser(config client.Config) bool {
} }
// Namespace implements KubeConfig // Namespace implements KubeConfig
func (config DirectClientConfig) Namespace() (string, bool, error) { func (config *DirectClientConfig) Namespace() (string, bool, error) {
if err := config.ConfirmUsable(); err != nil { if err := config.ConfirmUsable(); err != nil {
return "", false, err 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, // 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. // 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 := make([]error, 0)
validationErrors = append(validationErrors, validateAuthInfo(config.getAuthInfoName(), config.getAuthInfo())...) validationErrors = append(validationErrors, validateAuthInfo(config.getAuthInfoName(), config.getAuthInfo())...)
validationErrors = append(validationErrors, validateClusterInfo(config.getClusterName(), config.getCluster())...) validationErrors = append(validationErrors, validateClusterInfo(config.getClusterName(), config.getCluster())...)
@ -249,7 +249,7 @@ func (config DirectClientConfig) ConfirmUsable() error {
return newErrConfigurationInvalid(validationErrors) return newErrConfigurationInvalid(validationErrors)
} }
func (config DirectClientConfig) getContextName() string { func (config *DirectClientConfig) getContextName() string {
if len(config.overrides.CurrentContext) != 0 { if len(config.overrides.CurrentContext) != 0 {
return config.overrides.CurrentContext return config.overrides.CurrentContext
} }
@ -260,21 +260,21 @@ func (config DirectClientConfig) getContextName() string {
return config.config.CurrentContext return config.config.CurrentContext
} }
func (config DirectClientConfig) getAuthInfoName() string { func (config *DirectClientConfig) getAuthInfoName() string {
if len(config.overrides.Context.AuthInfo) != 0 { if len(config.overrides.Context.AuthInfo) != 0 {
return config.overrides.Context.AuthInfo return config.overrides.Context.AuthInfo
} }
return config.getContext().AuthInfo return config.getContext().AuthInfo
} }
func (config DirectClientConfig) getClusterName() string { func (config *DirectClientConfig) getClusterName() string {
if len(config.overrides.Context.Cluster) != 0 { if len(config.overrides.Context.Cluster) != 0 {
return config.overrides.Context.Cluster return config.overrides.Context.Cluster
} }
return config.getContext().Cluster return config.getContext().Cluster
} }
func (config DirectClientConfig) getContext() clientcmdapi.Context { func (config *DirectClientConfig) getContext() clientcmdapi.Context {
contexts := config.config.Contexts contexts := config.config.Contexts
contextName := config.getContextName() contextName := config.getContextName()
@ -287,7 +287,7 @@ func (config DirectClientConfig) getContext() clientcmdapi.Context {
return mergedContext return mergedContext
} }
func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { func (config *DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo {
authInfos := config.config.AuthInfos authInfos := config.config.AuthInfos
authInfoName := config.getAuthInfoName() authInfoName := config.getAuthInfoName()
@ -300,7 +300,7 @@ func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo {
return mergedAuthInfo return mergedAuthInfo
} }
func (config DirectClientConfig) getCluster() clientcmdapi.Cluster { func (config *DirectClientConfig) getCluster() clientcmdapi.Cluster {
clusterInfos := config.config.Clusters clusterInfos := config.config.Clusters
clusterInfoName := config.getClusterName() clusterInfoName := config.getClusterName()

View File

@ -117,23 +117,41 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
} else { } else {
kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) 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 // first merge all of our maps
mapConfig := clientcmdapi.NewConfig() mapConfig := clientcmdapi.NewConfig()
for _, file := range kubeConfigFiles { for _, kubeconfig := range kubeconfigs {
if err := mergeConfigWithFile(mapConfig, file); err != nil { mergo.Merge(mapConfig, kubeconfig)
errlist = append(errlist, err)
}
} }
// merge all of the struct values in the reverse order so that priority is given correctly // 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 // errors are not added to the list the second time
nonMapConfig := clientcmdapi.NewConfig() nonMapConfig := clientcmdapi.NewConfig()
for i := len(kubeConfigFiles) - 1; i >= 0; i-- { for i := len(kubeconfigs) - 1; i >= 0; i-- {
file := kubeConfigFiles[i] kubeconfig := kubeconfigs[i]
mergeConfigWithFile(nonMapConfig, file) 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 // 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 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 // 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

@ -19,6 +19,7 @@ package clientcmd
import ( import (
"io" "io"
"reflect" "reflect"
"sync"
"github.com/golang/glog" "github.com/golang/glog"
@ -35,19 +36,27 @@ type DeferredLoadingClientConfig struct {
loadingRules *ClientConfigLoadingRules loadingRules *ClientConfigLoadingRules
overrides *ConfigOverrides overrides *ConfigOverrides
fallbackReader io.Reader fallbackReader io.Reader
clientConfig ClientConfig
loadingLock sync.Mutex
} }
// NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name // NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name
func NewNonInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides) ClientConfig { 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 // NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader
func NewInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { 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) { 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() mergedConfig, err := config.loadingRules.Load()
if err != nil { if err != nil {
return nil, err return nil, err
@ -60,10 +69,14 @@ func (config DeferredLoadingClientConfig) createClientConfig() (ClientConfig, er
mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides) mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides)
} }
return mergedClientConfig, nil config.clientConfig = mergedClientConfig
}
}
return config.clientConfig, nil
} }
func (config DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) {
mergedConfig, err := config.createClientConfig() mergedConfig, err := config.createClientConfig()
if err != nil { if err != nil {
return clientcmdapi.Config{}, err return clientcmdapi.Config{}, err
@ -73,7 +86,7 @@ func (config DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, erro
} }
// ClientConfig implements ClientConfig // ClientConfig implements ClientConfig
func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) { func (config *DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) {
mergedClientConfig, err := config.createClientConfig() mergedClientConfig, err := config.createClientConfig()
if err != nil { if err != nil {
return nil, err return nil, err
@ -94,7 +107,7 @@ func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error)
} }
// Namespace implements KubeConfig // Namespace implements KubeConfig
func (config DeferredLoadingClientConfig) Namespace() (string, bool, error) { func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) {
mergedKubeConfig, err := config.createClientConfig() mergedKubeConfig, err := config.createClientConfig()
if err != nil { if err != nil {
return "", false, err return "", false, err