From 731ebf6f9a69206446fd9a1da53984dd9bcc78b7 Mon Sep 17 00:00:00 2001 From: TravisTX Date: Wed, 10 Feb 2021 19:23:00 -0700 Subject: [PATCH] feat: kubectl namespace --- docs/docs/segment-kubectl.md | 24 ++++++++++- src/segment_kubectl.go | 31 +++++++++++--- src/segment_kubectl_test.go | 78 ++++++++++++++++++++++-------------- 3 files changed, 96 insertions(+), 37 deletions(-) diff --git a/docs/docs/segment-kubectl.md b/docs/docs/segment-kubectl.md index 5a4c55df3bad..ec5234b71e8b 100644 --- a/docs/docs/segment-kubectl.md +++ b/docs/docs/segment-kubectl.md @@ -6,7 +6,7 @@ sidebar_label: Kubectl ## What -Display the currently active Kubernetes context name. +Display the currently active Kubernetes context name and namespace name. ## Sample Configuration @@ -18,7 +18,27 @@ Display the currently active Kubernetes context name. "foreground": "#000000", "background": "#ebcc34", "properties": { - "prefix": " \uFD31 " + "prefix": " \uFD31 ", + "template": "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}" } } ``` + +## Properties + +- template: `string` - A go [text/template][go-text-template] template utilizing the properties below. +Defaults to `{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}` + +## Template Properties + +- `.Context`: `string` - the current kubectl context +- `.Namespace`: `string` - the current kubectl namespace + +## Tips + +It is common for the Kubernetes "default" namespace to be used when no namespace is provided. If you want your prompt to + render an empty current namespace using the word "default", you can use something like this for the template: + +`{{.Context}} :: {{if .Namespace}}{{.Namespace}}{{else}}default{{end}}` + +[go-text-template]: https://golang.org/pkg/text/template/ diff --git a/src/segment_kubectl.go b/src/segment_kubectl.go index 52e889744566..7f0fc59ddbd4 100644 --- a/src/segment_kubectl.go +++ b/src/segment_kubectl.go @@ -1,13 +1,23 @@ package main +import ( + "strings" +) + type kubectl struct { - props *properties - env environmentInfo - contextName string + props *properties + env environmentInfo + Context string + Namespace string } func (k *kubectl) string() string { - return k.contextName + segmentTemplate := k.props.getString(SegmentTemplate, "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}") + template := &textTemplate{ + Template: segmentTemplate, + Context: k, + } + return template.render() } func (k *kubectl) init(props *properties, env environmentInfo) { @@ -20,6 +30,15 @@ func (k *kubectl) enabled() bool { if !k.env.hasCommand(cmd) { return false } - k.contextName, _ = k.env.runCommand(cmd, "config", "current-context") - return k.contextName != "" + result, err := k.env.runCommand(cmd, "config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace}") + if err != nil { + k.Context = "KUBECTL ERR" + k.Namespace = k.Context + return true + } + + values := strings.Split(result, ",") + k.Context = values[0] + k.Namespace = values[1] + return k.Context != "" } diff --git a/src/segment_kubectl_test.go b/src/segment_kubectl_test.go index 070e8759014a..55eb04fcad40 100644 --- a/src/segment_kubectl_test.go +++ b/src/segment_kubectl_test.go @@ -7,45 +7,65 @@ import ( ) type kubectlArgs struct { - enabled bool - contextName string + kubectlExists bool + kubectlErr bool + template string + context string + namespace string } func bootStrapKubectlTest(args *kubectlArgs) *kubectl { env := new(MockedEnvironment) - env.On("hasCommand", "kubectl").Return(args.enabled) - env.On("runCommand", "kubectl", []string{"config", "current-context"}).Return(args.contextName, nil) + env.On("hasCommand", "kubectl").Return(args.kubectlExists) + kubectlOut := args.context + "," + args.namespace + var kubectlErr error = nil + if args.kubectlErr { + kubectlErr = &commandError{ + err: "oops", + exitCode: 1, + } + } + env.On("runCommand", "kubectl", []string{"config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace}"}).Return(kubectlOut, kubectlErr) k := &kubectl{ - env: env, - props: &properties{}, + env: env, + props: &properties{ + values: map[Property]interface{}{ + SegmentTemplate: args.template, + }, + }, } return k } -func TestKubectlWriterDisabled(t *testing.T) { - args := &kubectlArgs{ - enabled: false, +func TestKubectlSegment(t *testing.T) { + standardTemplate := "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}" + cases := []struct { + Case string + Template string + KubectlExists bool + Context string + Namespace string + KubectlErr bool + ExpectedEnabled bool + ExpectedString string + }{ + {Case: "disabled", Template: standardTemplate, KubectlExists: false, Context: "aaa", Namespace: "bbb", ExpectedString: "", ExpectedEnabled: false}, + {Case: "normal", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "bbb", ExpectedString: "aaa :: bbb", ExpectedEnabled: true}, + {Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "", ExpectedString: "aaa", ExpectedEnabled: true}, + {Case: "kubectl error", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "bbb", KubectlErr: true, + ExpectedString: "KUBECTL ERR :: KUBECTL ERR", ExpectedEnabled: true}, } - kubectl := bootStrapKubectlTest(args) - assert.False(t, kubectl.enabled()) -} - -func TestKubectlEnabled(t *testing.T) { - expected := "context-name" - args := &kubectlArgs{ - enabled: true, - contextName: expected, - } - kubectl := bootStrapKubectlTest(args) - assert.True(t, kubectl.enabled()) - assert.Equal(t, expected, kubectl.string()) -} -func TestKubectlNoContext(t *testing.T) { - args := &kubectlArgs{ - enabled: true, - contextName: "", + for _, tc := range cases { + args := &kubectlArgs{ + kubectlExists: tc.KubectlExists, + template: tc.Template, + context: tc.Context, + namespace: tc.Namespace, + kubectlErr: tc.KubectlErr, + } + kubectl := bootStrapKubectlTest(args) + assert.Equal(t, tc.ExpectedEnabled, kubectl.enabled(), tc.Case) + assert.Equal(t, tc.ExpectedString, kubectl.string(), tc.Case) } - kubectl := bootStrapKubectlTest(args) - assert.False(t, kubectl.enabled()) }