diff --git a/pkg/client/clientcmd/loader.go b/pkg/client/clientcmd/loader.go index a75ea25e9f9..3eff8857756 100644 --- a/pkg/client/clientcmd/loader.go +++ b/pkg/client/clientcmd/loader.go @@ -33,31 +33,35 @@ import ( const ( RecommendedConfigPathFlag = "kubeconfig" RecommendedConfigPathEnvVar = "KUBECONFIG" + + DefaultEnvVarIndex = 0 + DefaultCurrentDirIndex = 1 + DefaultHomeDirIndex = 2 ) -// ClientConfigLoadingRules is a struct that calls our 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: +// [0] = EnvVarPath +// [1] = CurrentDirectoryPath +// [2] = HomeDirectoryPath +// ExplicitPath is special, because if a user specifically requests a certain file be used and error is reported if thie file is not present type ClientConfigLoadingRules struct { - CommandLinePath string - EnvVarPath string - CurrentDirectoryPath string - HomeDirectoryPath string + ExplicitPath string + Precedence []string } -// NewClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in. You are not required to +// NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in. You are not required to // use this constructor -func NewClientConfigLoadingRules() *ClientConfigLoadingRules { +func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules { return &ClientConfigLoadingRules{ - CurrentDirectoryPath: ".kubeconfig", - HomeDirectoryPath: os.Getenv("HOME") + "/.kube/.kubeconfig", + Precedence: []string{os.Getenv(RecommendedConfigPathEnvVar), ".kubeconfig", os.Getenv("HOME") + "/.kube/.kubeconfig"}, } } // Load takes the loading rules and merges together a Config object based on following order. -// 1. CommandLinePath -// 2. EnvVarPath -// 3. CurrentDirectoryPath -// 4. HomeDirectoryPath -// A missing CommandLinePath file produces an error. Empty filenames or other missing files are ignored. +// 1. ExplicitPath +// 2. Precedence slice +// A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored. // Read errors or files with non-deserializable content produce errors. // The first file to set a particular map key wins and map key's value is never changed. // BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed. @@ -71,13 +75,14 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { errlist := []error{} // Make sure a file we were explicitly told to use exists - if len(rules.CommandLinePath) > 0 { - if _, err := os.Stat(rules.CommandLinePath); os.IsNotExist(err) { - errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.CommandLinePath)) + if len(rules.ExplicitPath) > 0 { + if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) { + errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.ExplicitPath)) } } - kubeConfigFiles := []string{rules.CommandLinePath, rules.EnvVarPath, rules.CurrentDirectoryPath, rules.HomeDirectoryPath} + kubeConfigFiles := []string{rules.ExplicitPath} + kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) // first merge all of our maps mapConfig := clientcmdapi.NewConfig() @@ -85,7 +90,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { if err := mergeConfigWithFile(mapConfig, file); err != nil { errlist = append(errlist, err) } - if err := resolveLocalPaths(file, mapConfig); err != nil { + if err := ResolveLocalPaths(file, mapConfig); err != nil { errlist = append(errlist, err) } } @@ -96,7 +101,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { for i := len(kubeConfigFiles) - 1; i >= 0; i-- { file := kubeConfigFiles[i] mergeConfigWithFile(nonMapConfig, file) - resolveLocalPaths(file, nonMapConfig) + ResolveLocalPaths(file, nonMapConfig) } // since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and @@ -127,10 +132,10 @@ func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) e return nil } -// resolveLocalPaths resolves all relative paths in the config object with respect to the parent directory of the filename +// ResolveLocalPaths resolves all relative paths in the config object with respect to the parent directory of the filename // this cannot be done directly inside of LoadFromFile because doing so there would make it impossible to load a file without // modification of its contents. -func resolveLocalPaths(filename string, config *clientcmdapi.Config) error { +func ResolveLocalPaths(filename string, config *clientcmdapi.Config) error { if len(filename) == 0 { return nil } diff --git a/pkg/client/clientcmd/loader_test.go b/pkg/client/clientcmd/loader_test.go index 17e91c75b58..d9f30ba26f3 100644 --- a/pkg/client/clientcmd/loader_test.go +++ b/pkg/client/clientcmd/loader_test.go @@ -78,7 +78,7 @@ var ( func TestNonExistentCommandLineFile(t *testing.T) { loadingRules := ClientConfigLoadingRules{ - CommandLinePath: "bogus_file", + ExplicitPath: "bogus_file", } _, err := loadingRules.Load() @@ -92,9 +92,7 @@ func TestNonExistentCommandLineFile(t *testing.T) { func TestToleratingMissingFiles(t *testing.T) { loadingRules := ClientConfigLoadingRules{ - EnvVarPath: "bogus1", - CurrentDirectoryPath: "bogus2", - HomeDirectoryPath: "bogus3", + Precedence: []string{"bogus1", "bogus2", "bogus3"}, } _, err := loadingRules.Load() @@ -112,7 +110,7 @@ func TestErrorReadingFile(t *testing.T) { } loadingRules := ClientConfigLoadingRules{ - CommandLinePath: commandLineFile.Name(), + ExplicitPath: commandLineFile.Name(), } _, err := loadingRules.Load() @@ -132,7 +130,7 @@ func TestErrorReadingNonFile(t *testing.T) { defer os.Remove(tmpdir) loadingRules := ClientConfigLoadingRules{ - CommandLinePath: tmpdir, + ExplicitPath: tmpdir, } _, err = loadingRules.Load() @@ -161,8 +159,8 @@ func TestConflictingCurrentContext(t *testing.T) { WriteToFile(mockEnvVarConfig, envVarFile.Name()) loadingRules := ClientConfigLoadingRules{ - CommandLinePath: commandLineFile.Name(), - EnvVarPath: envVarFile.Name(), + ExplicitPath: commandLineFile.Name(), + Precedence: []string{envVarFile.Name()}, } mergedConfig, err := loadingRules.Load() @@ -211,8 +209,8 @@ func TestResolveRelativePaths(t *testing.T) { WriteToFile(pathResolutionConfig2, configFile2) loadingRules := ClientConfigLoadingRules{ - CommandLinePath: configFile1, - EnvVarPath: configFile2, + ExplicitPath: configFile1, + Precedence: []string{configFile2}, } mergedConfig, err := loadingRules.Load() @@ -286,8 +284,8 @@ func ExampleMergingSomeWithConflict() { WriteToFile(testConfigConflictAlfa, envVarFile.Name()) loadingRules := ClientConfigLoadingRules{ - CommandLinePath: commandLineFile.Name(), - EnvVarPath: envVarFile.Name(), + ExplicitPath: commandLineFile.Name(), + Precedence: []string{envVarFile.Name()}, } mergedConfig, err := loadingRules.Load() @@ -346,10 +344,8 @@ func ExampleMergingEverythingNoConflicts() { WriteToFile(testConfigDelta, homeDirFile.Name()) loadingRules := ClientConfigLoadingRules{ - CommandLinePath: commandLineFile.Name(), - EnvVarPath: envVarFile.Name(), - CurrentDirectoryPath: currentDirFile.Name(), - HomeDirectoryPath: homeDirFile.Name(), + ExplicitPath: commandLineFile.Name(), + Precedence: []string{envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()}, } mergedConfig, err := loadingRules.Load() diff --git a/pkg/client/clientcmd/merged_client_builder_test.go b/pkg/client/clientcmd/merged_client_builder_test.go index 2c659ebfc7b..266bd3d386a 100644 --- a/pkg/client/clientcmd/merged_client_builder_test.go +++ b/pkg/client/clientcmd/merged_client_builder_test.go @@ -78,11 +78,11 @@ func testWriteAuthInfoFile(auth clientauth.Info, filename string) error { } func testBindClientConfig(cmd *cobra.Command) ClientConfig { - loadingRules := NewClientConfigLoadingRules() - loadingRules.EnvVarPath = "" - loadingRules.HomeDirectoryPath = "" - loadingRules.CurrentDirectoryPath = "" - cmd.PersistentFlags().StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") + loadingRules := NewDefaultClientConfigLoadingRules() + loadingRules.Precedence[DefaultEnvVarIndex] = "" + loadingRules.Precedence[DefaultCurrentDirIndex] = "" + loadingRules.Precedence[DefaultHomeDirIndex] = "" + cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") overrides := &ConfigOverrides{} BindOverrideFlags(overrides, cmd.PersistentFlags(), RecommendedConfigOverrideFlags("")) diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index f082a651c4d..fd23142b4d8 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -322,9 +322,8 @@ func (f *Factory) ClientMapperForCommand(cmd *cobra.Command) resource.ClientMapp // 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 func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { - loadingRules := clientcmd.NewClientConfigLoadingRules() - loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") overrides := &clientcmd.ConfigOverrides{} flagNames := clientcmd.RecommendedConfigOverrideFlags("") diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index 82cf3f83a76..18fc4afe79d 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -19,7 +19,6 @@ package config import ( "fmt" "io" - "os" "github.com/golang/glog" "github.com/spf13/cobra" @@ -113,9 +112,8 @@ func (o viewOptions) validate() error { func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { switch { case o.merge.Value(): - loadingRules := clientcmd.NewClientConfigLoadingRules() - loadingRules.EnvVarPath = os.Getenv("KUBECONFIG") - loadingRules.CommandLinePath = o.pathOptions.specifiedFile + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + loadingRules.ExplicitPath = o.pathOptions.specifiedFile overrides := &clientcmd.ConfigOverrides{} clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)