allow any number of kubeconfig files

This commit is contained in:
deads2k 2015-03-17 08:21:10 -04:00
parent 9df2ea4aef
commit 8555ed3ede
5 changed files with 48 additions and 50 deletions

View File

@ -33,31 +33,35 @@ import (
const ( const (
RecommendedConfigPathFlag = "kubeconfig" RecommendedConfigPathFlag = "kubeconfig"
RecommendedConfigPathEnvVar = "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 { type ClientConfigLoadingRules struct {
CommandLinePath string ExplicitPath string
EnvVarPath string Precedence []string
CurrentDirectoryPath string
HomeDirectoryPath 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 // use this constructor
func NewClientConfigLoadingRules() *ClientConfigLoadingRules { func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules {
return &ClientConfigLoadingRules{ return &ClientConfigLoadingRules{
CurrentDirectoryPath: ".kubeconfig", Precedence: []string{os.Getenv(RecommendedConfigPathEnvVar), ".kubeconfig", os.Getenv("HOME") + "/.kube/.kubeconfig"},
HomeDirectoryPath: os.Getenv("HOME") + "/.kube/.kubeconfig",
} }
} }
// Load takes the loading rules and merges together a Config object based on following order. // Load takes the loading rules and merges together a Config object based on following order.
// 1. CommandLinePath // 1. ExplicitPath
// 2. EnvVarPath // 2. Precedence slice
// 3. CurrentDirectoryPath // A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored.
// 4. HomeDirectoryPath
// A missing CommandLinePath file produces an error. Empty filenames or other missing files are ignored.
// Read errors or files with non-deserializable content produce errors. // 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. // 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. // 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{} errlist := []error{}
// Make sure a file we were explicitly told to use exists // Make sure a file we were explicitly told to use exists
if len(rules.CommandLinePath) > 0 { if len(rules.ExplicitPath) > 0 {
if _, err := os.Stat(rules.CommandLinePath); os.IsNotExist(err) { if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.CommandLinePath)) 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 // first merge all of our maps
mapConfig := clientcmdapi.NewConfig() mapConfig := clientcmdapi.NewConfig()
@ -85,7 +90,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
if err := mergeConfigWithFile(mapConfig, file); err != nil { if err := mergeConfigWithFile(mapConfig, file); err != nil {
errlist = append(errlist, err) errlist = append(errlist, err)
} }
if err := resolveLocalPaths(file, mapConfig); err != nil { if err := ResolveLocalPaths(file, mapConfig); err != nil {
errlist = append(errlist, err) errlist = append(errlist, err)
} }
} }
@ -96,7 +101,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
for i := len(kubeConfigFiles) - 1; i >= 0; i-- { for i := len(kubeConfigFiles) - 1; i >= 0; i-- {
file := kubeConfigFiles[i] file := kubeConfigFiles[i]
mergeConfigWithFile(nonMapConfig, file) 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 // 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 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 // 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. // modification of its contents.
func resolveLocalPaths(filename string, config *clientcmdapi.Config) error { func ResolveLocalPaths(filename string, config *clientcmdapi.Config) error {
if len(filename) == 0 { if len(filename) == 0 {
return nil return nil
} }

View File

@ -78,7 +78,7 @@ var (
func TestNonExistentCommandLineFile(t *testing.T) { func TestNonExistentCommandLineFile(t *testing.T) {
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: "bogus_file", ExplicitPath: "bogus_file",
} }
_, err := loadingRules.Load() _, err := loadingRules.Load()
@ -92,9 +92,7 @@ func TestNonExistentCommandLineFile(t *testing.T) {
func TestToleratingMissingFiles(t *testing.T) { func TestToleratingMissingFiles(t *testing.T) {
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
EnvVarPath: "bogus1", Precedence: []string{"bogus1", "bogus2", "bogus3"},
CurrentDirectoryPath: "bogus2",
HomeDirectoryPath: "bogus3",
} }
_, err := loadingRules.Load() _, err := loadingRules.Load()
@ -112,7 +110,7 @@ func TestErrorReadingFile(t *testing.T) {
} }
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: commandLineFile.Name(), ExplicitPath: commandLineFile.Name(),
} }
_, err := loadingRules.Load() _, err := loadingRules.Load()
@ -132,7 +130,7 @@ func TestErrorReadingNonFile(t *testing.T) {
defer os.Remove(tmpdir) defer os.Remove(tmpdir)
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: tmpdir, ExplicitPath: tmpdir,
} }
_, err = loadingRules.Load() _, err = loadingRules.Load()
@ -161,8 +159,8 @@ func TestConflictingCurrentContext(t *testing.T) {
WriteToFile(mockEnvVarConfig, envVarFile.Name()) WriteToFile(mockEnvVarConfig, envVarFile.Name())
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: commandLineFile.Name(), ExplicitPath: commandLineFile.Name(),
EnvVarPath: envVarFile.Name(), Precedence: []string{envVarFile.Name()},
} }
mergedConfig, err := loadingRules.Load() mergedConfig, err := loadingRules.Load()
@ -211,8 +209,8 @@ func TestResolveRelativePaths(t *testing.T) {
WriteToFile(pathResolutionConfig2, configFile2) WriteToFile(pathResolutionConfig2, configFile2)
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: configFile1, ExplicitPath: configFile1,
EnvVarPath: configFile2, Precedence: []string{configFile2},
} }
mergedConfig, err := loadingRules.Load() mergedConfig, err := loadingRules.Load()
@ -286,8 +284,8 @@ func ExampleMergingSomeWithConflict() {
WriteToFile(testConfigConflictAlfa, envVarFile.Name()) WriteToFile(testConfigConflictAlfa, envVarFile.Name())
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: commandLineFile.Name(), ExplicitPath: commandLineFile.Name(),
EnvVarPath: envVarFile.Name(), Precedence: []string{envVarFile.Name()},
} }
mergedConfig, err := loadingRules.Load() mergedConfig, err := loadingRules.Load()
@ -346,10 +344,8 @@ func ExampleMergingEverythingNoConflicts() {
WriteToFile(testConfigDelta, homeDirFile.Name()) WriteToFile(testConfigDelta, homeDirFile.Name())
loadingRules := ClientConfigLoadingRules{ loadingRules := ClientConfigLoadingRules{
CommandLinePath: commandLineFile.Name(), ExplicitPath: commandLineFile.Name(),
EnvVarPath: envVarFile.Name(), Precedence: []string{envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()},
CurrentDirectoryPath: currentDirFile.Name(),
HomeDirectoryPath: homeDirFile.Name(),
} }
mergedConfig, err := loadingRules.Load() mergedConfig, err := loadingRules.Load()

View File

@ -78,11 +78,11 @@ func testWriteAuthInfoFile(auth clientauth.Info, filename string) error {
} }
func testBindClientConfig(cmd *cobra.Command) ClientConfig { func testBindClientConfig(cmd *cobra.Command) ClientConfig {
loadingRules := NewClientConfigLoadingRules() loadingRules := NewDefaultClientConfigLoadingRules()
loadingRules.EnvVarPath = "" loadingRules.Precedence[DefaultEnvVarIndex] = ""
loadingRules.HomeDirectoryPath = "" loadingRules.Precedence[DefaultCurrentDirIndex] = ""
loadingRules.CurrentDirectoryPath = "" loadingRules.Precedence[DefaultHomeDirIndex] = ""
cmd.PersistentFlags().StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &ConfigOverrides{} overrides := &ConfigOverrides{}
BindOverrideFlags(overrides, cmd.PersistentFlags(), RecommendedConfigOverrideFlags("")) BindOverrideFlags(overrides, cmd.PersistentFlags(), RecommendedConfigOverrideFlags(""))

View File

@ -314,9 +314,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. // 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 // 2. Use default values and potentially prompt for auth information
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewClientConfigLoadingRules() loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &clientcmd.ConfigOverrides{} overrides := &clientcmd.ConfigOverrides{}
flagNames := clientcmd.RecommendedConfigOverrideFlags("") flagNames := clientcmd.RecommendedConfigOverrideFlags("")

View File

@ -19,7 +19,6 @@ package config
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -113,9 +112,8 @@ func (o viewOptions) validate() error {
func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) {
switch { switch {
case o.merge.Value(): case o.merge.Value():
loadingRules := clientcmd.NewClientConfigLoadingRules() loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.EnvVarPath = os.Getenv("KUBECONFIG") loadingRules.ExplicitPath = o.pathOptions.specifiedFile
loadingRules.CommandLinePath = o.pathOptions.specifiedFile
overrides := &clientcmd.ConfigOverrides{} overrides := &clientcmd.ConfigOverrides{}
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)