Skip to content

feat: handle path list for kubeconfig env var #510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion klient/conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"os/user"
"path"
"path/filepath"

"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -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 (
Expand Down
90 changes: 86 additions & 4 deletions klient/conf/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package conf

import (
"fmt"
"os"
"path/filepath"
"testing"

Expand All @@ -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 {
Expand Down
34 changes: 28 additions & 6 deletions klient/conf/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
}
Expand Down