diff --git a/klient/conf/config.go b/klient/conf/config.go index 5e57f80f..b12bcb7c 100644 --- a/klient/conf/config.go +++ b/klient/conf/config.go @@ -23,6 +23,7 @@ import ( "os" "os/user" "path" + "path/filepath" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -97,7 +98,15 @@ func ResolveKubeConfigFile() string { // if KUBECONFIG env is defined then use that kubeConfigPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) if kubeConfigPath != "" { - return kubeConfigPath + // handle the variable as a path list, similar to client-go. + filePaths := filepath.SplitList(kubeConfigPath) + for _, filename := range filePaths { + if _, err := os.Stat(filename); err == nil { + return filename + } + } + // return the last path in the list if none exist yet. + return filePaths[len(filePaths)-1] } var ( diff --git a/klient/conf/config_test.go b/klient/conf/config_test.go index b0ef5d7c..17bc62c5 100644 --- a/klient/conf/config_test.go +++ b/klient/conf/config_test.go @@ -17,6 +17,8 @@ limitations under the License. package conf import ( + "fmt" + "os" "path/filepath" "testing" @@ -25,15 +27,95 @@ import ( var kubeconfig string -func TestResolveKubeConfigFile(t *testing.T) { - home := homedir.HomeDir() +func TestResolveKubeConfigFileFlag(t *testing.T) { filename := ResolveKubeConfigFile() - - if filename != filepath.Join(home, "test", ".kube", "config") { + if filename != kubeconfigpath { t.Errorf("unexpected config path: %s", filename) } } +func TestResolveKubeConfigFileEnv(t *testing.T) { + // NOTE: not considered safe to run in parallel with other tests thats + // require the --kubeconfig and --kubecontext flags. + clearKubeconfigFlags() + defer setKubeconfigFlags() + + kubeConfigPath1 := filepath.Join(t.TempDir(), "config") + if _, err := os.Create(kubeConfigPath1); err != nil { + t.Errorf("failed to create kubeconfig: %v", err) + } + + kubeConfigPath2 := filepath.Join(t.TempDir(), "config") + if _, err := os.Create(kubeConfigPath2); err != nil { + t.Errorf("failed to create kubeconfig: %v", err) + } + + t.Run("WithEnvEmpty", func(t *testing.T) { + t.Setenv("KUBECONFIG", "") + + filename := ResolveKubeConfigFile() + + // this will fallback to the true home directory. + if filename != filepath.Join(homedir.HomeDir(), ".kube", "config") { + t.Errorf("unexpected config path: %s", filename) + } + }) + + t.Run("WithEnvPath", func(t *testing.T) { + t.Setenv("KUBECONFIG", kubeConfigPath1) + + filename := ResolveKubeConfigFile() + + if filename != kubeConfigPath1 { + t.Errorf("unexpected config path: %s", filename) + } + }) + + t.Run("WithEnvPathListAllExist", func(t *testing.T) { + t.Setenv("KUBECONFIG", fmt.Sprintf("%s:%s", kubeConfigPath1, kubeConfigPath2)) + + filename := ResolveKubeConfigFile() + + // if all exist then it will take the first. + if filename != kubeConfigPath1 { + t.Errorf("unexpected config path: %s", filename) + } + }) + + t.Run("WithEnvPathListFirstExists", func(t *testing.T) { + t.Setenv("KUBECONFIG", fmt.Sprintf("%s:fake", kubeConfigPath1)) + + filename := ResolveKubeConfigFile() + + // if first exists then it will take the first. + if filename != kubeConfigPath1 { + t.Errorf("unexpected config path: %s", filename) + } + }) + + t.Run("WithEnvPathListLastExists", func(t *testing.T) { + t.Setenv("KUBECONFIG", fmt.Sprintf("%s:fake", kubeConfigPath1)) + + filename := ResolveKubeConfigFile() + + // if only last exists then it will take the last. + if filename != kubeConfigPath1 { + t.Errorf("unexpected config path: %s", filename) + } + }) + + t.Run("WithEnvPathListNoneExist", func(t *testing.T) { + t.Setenv("KUBECONFIG", "fake-foo:fake-bar") + + filename := ResolveKubeConfigFile() + + // if none exist then it will take the last. + if filename != "fake-bar" { + t.Errorf("unexpected config path: %s", filename) + } + }) +} + func TestNew(t *testing.T) { cfg, err := New(ResolveKubeConfigFile()) if err != nil { diff --git a/klient/conf/main_test.go b/klient/conf/main_test.go index eeb5dcb0..efa5657f 100644 --- a/klient/conf/main_test.go +++ b/klient/conf/main_test.go @@ -28,8 +28,14 @@ import ( "k8s.io/client-go/util/homedir" ) +var ( + kubeconfigpath string + kubeContext string +) + func TestMain(m *testing.M) { setup() + setKubeconfigFlags() code := m.Run() teardown() os.Exit(code) @@ -39,8 +45,8 @@ func setup() { home := homedir.HomeDir() kubeconfigdir := filepath.Join(home, "test", ".kube") - kubeconfigpath := filepath.Join(kubeconfigdir, "config") - kubeContext := "test-context" + kubeconfigpath = filepath.Join(kubeconfigdir, "config") + kubeContext = "test-context" // check if file exists _, err := os.Stat(kubeconfigpath) @@ -66,17 +72,33 @@ func setup() { flag.StringVar(&kubeconfig, "kubeconfig", "", "Paths to a kubeconfig. Only required if out-of-cluster.") flag.StringVar(&kubeContext, "context", "", "The name of the kubeconfig context to use. Only required if out-of-cluster.") +} +func setKubeconfigFlags() { // set --kubeconfig flag - err = flag.Set("kubeconfig", kubeconfigpath) - if err != nil { + if err := flag.Set("kubeconfig", kubeconfigpath); err != nil { log.ErrorS(err, "unexpected error while setting kubeconfig flag value") return } // set --context flag - err = flag.Set("context", kubeContext) - if err != nil { + if err := flag.Set("context", kubeContext); err != nil { + log.ErrorS(err, "unexpected error while setting context flag value") + return + } + + flag.Parse() +} + +func clearKubeconfigFlags() { + // clear --kubeconfig flag + if err := flag.Set("kubeconfig", ""); err != nil { + log.ErrorS(err, "unexpected error while setting kubeconfig flag value") + return + } + + // clear --context flag + if err := flag.Set("context", ""); err != nil { log.ErrorS(err, "unexpected error while setting context flag value") return }