diff --git a/src/segment_kubectl.go b/src/segment_kubectl.go index d9f86653..71d29eaf 100644 --- a/src/segment_kubectl.go +++ b/src/segment_kubectl.go @@ -2,7 +2,6 @@ package main import ( "path/filepath" - "strings" "gopkg.in/yaml.v3" ) @@ -14,23 +13,23 @@ type kubectl struct { props properties env environmentInfo Context string - KubeConfigContext -} - -type KubeConfigContext struct { - Cluster string `yaml:"cluster"` - User string `yaml:"user"` - Namespace string `yaml:"namespace"` + KubeContext } type KubeConfig struct { CurrentContext string `yaml:"current-context"` Contexts []struct { - Context KubeConfigContext `yaml:"context"` - Name string `yaml:"name"` + Context *KubeContext `yaml:"context"` + Name string `yaml:"name"` } `yaml:"contexts"` } +type KubeContext struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace"` +} + func (k *kubectl) string() string { segmentTemplate := k.props.getString(SegmentTemplate, "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}") template := &textTemplate{ @@ -65,7 +64,7 @@ func (k *kubectl) doParseKubeConfig() bool { if len(kubeconfigs) == 0 { kubeconfigs = []string{filepath.Join(k.env.homeDir(), ".kube/config")} } - contexts := make(map[string]KubeConfigContext) + contexts := make(map[string]*KubeContext) k.Context = "" for _, kubeconfig := range kubeconfigs { if len(kubeconfig) == 0 { @@ -91,10 +90,13 @@ func (k *kubectl) doParseKubeConfig() bool { } context, exists := contexts[k.Context] - if exists { - k.KubeConfigContext = context - return true + if !exists { + continue } + if context != nil { + k.KubeContext = *context + } + return true } displayError := k.props.getBool(DisplayError, false) @@ -110,7 +112,7 @@ func (k *kubectl) doCallKubectl() bool { if !k.env.hasCommand(cmd) { return false } - result, err := k.env.runCommand(cmd, "config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace},{..context.user},{..context.cluster}") + result, err := k.env.runCommand(cmd, "config", "view", "--output", "yaml", "--minify") displayError := k.props.getBool(DisplayError, false) if err != nil && displayError { k.setError("KUBECTL ERR") @@ -120,15 +122,16 @@ func (k *kubectl) doCallKubectl() bool { return false } - values := strings.Split(result, ",") - if len(values) < 4 { + var config KubeConfig + err = yaml.Unmarshal([]byte(result), &config) + if err != nil { return false } - k.Context = values[0] - k.Namespace = values[1] - k.User = values[2] - k.Cluster = values[3] - return len(k.Context) > 0 + k.Context = config.CurrentContext + if len(config.Contexts) > 0 { + k.KubeContext = *config.Contexts[0].Context + } + return true } func (k *kubectl) setError(message string) { diff --git a/src/segment_kubectl_test.go b/src/segment_kubectl_test.go index 76a59458..34d69f4a 100644 --- a/src/segment_kubectl_test.go +++ b/src/segment_kubectl_test.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "io/ioutil" "path/filepath" "testing" @@ -29,16 +31,16 @@ func TestKubectlSegment(t *testing.T) { ExpectedString string Files map[string]string }{ - {Case: "disabled", Template: standardTemplate, KubectlExists: false, Context: "aaa", Namespace: "bbb", ExpectedString: "", ExpectedEnabled: false}, { - Case: "not enough arguments", - Template: standardTemplate, - KubectlExists: true, - Context: "aaa", - Namespace: "bbb", - ExpectedString: "", - ExpectedEnabled: false, + Case: "kubeconfig incomplete", + Template: testKubectlAllInfoTemplate, + ParseKubeConfig: true, + Kubeconfig: "currentcontextmarker" + lsep + "contextdefinitionincomplete", + Files: testKubeConfigFiles, + ExpectedString: "ctx :: :: :: ", + ExpectedEnabled: true, }, + {Case: "disabled", Template: standardTemplate, KubectlExists: false, Context: "aaa", Namespace: "bbb", ExpectedEnabled: false}, { Case: "all information", Template: testKubectlAllInfoTemplate, @@ -50,7 +52,7 @@ func TestKubectlSegment(t *testing.T) { ExpectedString: "aaa :: bbb :: ccc :: ddd", ExpectedEnabled: true, }, - {Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "", ExpectedString: "", ExpectedEnabled: false}, + {Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", ExpectedString: "aaa", ExpectedEnabled: true}, { Case: "kubectl error", Template: standardTemplate, @@ -100,30 +102,16 @@ func TestKubectlSegment(t *testing.T) { ExpectedString: "KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR", ExpectedEnabled: true, }, - { - Case: "kubeconfig incomplete", - Template: testKubectlAllInfoTemplate, - ParseKubeConfig: true, - Kubeconfig: "currentcontextmarker" + lsep + "contextdefinitionincomplete", - Files: testKubeConfigFiles, - ExpectedString: "ctx :: :: :: ", - ExpectedEnabled: true, - }, } for _, tc := range cases { env := new(MockedEnvironment) env.On("hasCommand", "kubectl").Return(tc.KubectlExists) - addCommaAndvalue := func(s string) string { - if s == "" { - return "" - } - return "," + s + var kubeconfig string + content, err := ioutil.ReadFile("./test/kubectl.yml") + if err == nil { + kubeconfig = fmt.Sprintf(string(content), tc.Cluster, tc.User, tc.Namespace, tc.Context) } - kubectlOut := tc.Context - kubectlOut += addCommaAndvalue(tc.Namespace) - kubectlOut += addCommaAndvalue(tc.User) - kubectlOut += addCommaAndvalue(tc.Cluster) var kubectlErr error if tc.KubectlErr { kubectlErr = &commandError{ @@ -131,9 +119,7 @@ func TestKubectlSegment(t *testing.T) { exitCode: 1, } } - env.On("runCommand", "kubectl", - []string{"config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace},{..context.user},{..context.cluster}"}).Return(kubectlOut, kubectlErr) - + env.On("runCommand", "kubectl", []string{"config", "view", "--output", "yaml", "--minify"}).Return(kubeconfig, kubectlErr) env.On("getenv", "KUBECONFIG").Return(tc.Kubeconfig) for path, content := range tc.Files { env.On("getFileContent", path).Return(content) diff --git a/src/test/kubectl.yml b/src/test/kubectl.yml new file mode 100644 index 00000000..a32f409c --- /dev/null +++ b/src/test/kubectl.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +clusters: null +contexts: + - context: + cluster: '%s' + user: '%s' + namespace: '%s' + name: jan +current-context: '%s' +kind: Config +preferences: {} +users: null