oh-my-posh/src/segment_kubectl_test.go
Stefan Hacker 7a73bcff0b feat: enable kubectl segment to read kubeconfig
This extends the kubectl segment to optionally not rely on the
kubectl command and instead to directly parse kubeconfig files like
kubectl does. This is meant as a performance optimization similar
to how the git segment can determine the current branch itself
without calling to git. Especially on Windows and in the presence
other factors slowing process creation like like AntiVirus this
can make shells using the segment considerably more responsive.

The functionality is enabled using the new parse_kubeconfig prop.
It defaults to false to prevent breaking existing users in case
there are any unanticipated behavioral changes.

Additionally the new template properties Cluster and User were
added as they are easily available and helpful in kubectl
setups with more elaborate configuration.
2021-11-26 18:22:40 +01:00

164 lines
5.9 KiB
Go

package main
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
type kubectlArgs struct {
kubectlExists bool
kubectlErr bool
kubeconfig string
parseKubeConfig bool
template string
displayError bool
kubectlOutContext string
kubectlOutNamespace string
kubectlOutUser string
kubectlOutCluster string
files map[string]string
}
const testKubectlAllInfoTemplate = "{{.Context}} :: {{.Namespace}} :: {{.User}} :: {{.Cluster}}"
func bootStrapKubectlTest(args *kubectlArgs) *kubectl {
env := new(MockedEnvironment)
env.On("hasCommand", "kubectl").Return(args.kubectlExists)
kubectlOut := args.kubectlOutContext + "," + args.kubectlOutNamespace + "," + args.kubectlOutUser + "," + args.kubectlOutCluster
var kubectlErr error
if args.kubectlErr {
kubectlErr = &commandError{
err: "oops",
exitCode: 1,
}
}
env.On("runCommand", "kubectl",
[]string{"config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace},{..context.user},{..context.cluster}"}).Return(kubectlOut, kubectlErr)
env.On("getenv", "KUBECONFIG").Return(args.kubeconfig)
for path, content := range args.files {
env.On("getFileContent", path).Return(content)
}
env.On("homeDir", nil).Return("testhome")
k := &kubectl{
env: env,
props: map[Property]interface{}{
SegmentTemplate: args.template,
DisplayError: args.displayError,
ParseKubeConfig: args.parseKubeConfig,
},
}
return k
}
func TestKubectlSegment(t *testing.T) {
standardTemplate := "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}"
lsep := string(filepath.ListSeparator)
cases := []struct {
Case string
Template string
DisplayError bool
KubectlExists bool
Kubeconfig string
ParseKubeConfig bool
Context string
Namespace string
User string
Cluster string
KubectlErr bool
ExpectedEnabled bool
ExpectedString string
Files map[string]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: "all information", Template: testKubectlAllInfoTemplate, KubectlExists: true, Context: "aaa", Namespace: "bbb", User: "ccc", Cluster: "ddd",
ExpectedString: "aaa :: bbb :: ccc :: ddd", ExpectedEnabled: true},
{Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "", ExpectedString: "aaa", ExpectedEnabled: true},
{Case: "kubectl error", Template: standardTemplate, DisplayError: true, KubectlExists: true, Context: "aaa", Namespace: "bbb", KubectlErr: true,
ExpectedString: "KUBECTL ERR :: KUBECTL ERR", ExpectedEnabled: true},
{Case: "kubectl error hidden", Template: standardTemplate, DisplayError: false, KubectlExists: true, Context: "aaa", Namespace: "bbb", KubectlErr: true, ExpectedEnabled: false},
{Case: "kubeconfig home", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true, Files: testKubeConfigFiles, ExpectedString: "aaa :: bbb :: ccc :: ddd",
ExpectedEnabled: true},
{Case: "kubeconfig multiple current marker first", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
Kubeconfig: "" + lsep + "currentcontextmarker" + lsep + "contextdefinition" + lsep + "contextredefinition",
Files: testKubeConfigFiles, ExpectedString: "ctx :: ns :: usr :: cl", ExpectedEnabled: true},
{Case: "kubeconfig multiple context first", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
Kubeconfig: "contextdefinition" + lsep + "contextredefinition" + lsep + "currentcontextmarker" + lsep,
Files: testKubeConfigFiles, ExpectedString: "ctx :: ns :: usr :: cl", ExpectedEnabled: true},
{Case: "kubeconfig error hidden", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true, Kubeconfig: "invalid", Files: testKubeConfigFiles, ExpectedEnabled: false},
{Case: "kubeconfig error", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
Kubeconfig: "invalid", Files: testKubeConfigFiles, DisplayError: true,
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 {
args := &kubectlArgs{
kubectlExists: tc.KubectlExists,
template: tc.Template,
displayError: tc.DisplayError,
kubectlOutContext: tc.Context,
kubectlOutNamespace: tc.Namespace,
kubectlOutUser: tc.User,
kubectlOutCluster: tc.Cluster,
kubectlErr: tc.KubectlErr,
parseKubeConfig: tc.ParseKubeConfig,
files: tc.Files,
kubeconfig: tc.Kubeconfig,
}
kubectl := bootStrapKubectlTest(args)
assert.Equal(t, tc.ExpectedEnabled, kubectl.enabled(), tc.Case)
if tc.ExpectedEnabled {
assert.Equal(t, tc.ExpectedString, kubectl.string(), tc.Case)
}
}
}
var testKubeConfigFiles = map[string]string{
filepath.Join("testhome", ".kube/config"): `
apiVersion: v1
contexts:
- context:
cluster: ddd
user: ccc
namespace: bbb
name: aaa
current-context: aaa
`,
"contextdefinition": `
apiVersion: v1
contexts:
- context:
cluster: cl
user: usr
namespace: ns
name: ctx
`,
"currentcontextmarker": `
apiVersion: v1
current-context: ctx
`,
"invalid": "this is not yaml",
"contextdefinitionincomplete": `
apiVersion: v1
contexts:
- name: ctx
`,
"contextredefinition": `
apiVersion: v1
contexts:
- context:
cluster: wrongcl
user: wrongu
namespace: wrongns
name: ctx
`,
}