mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-02-02 05:41:10 -08:00
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.
139 lines
3.2 KiB
Go
139 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Whether to use kubectl or read kubeconfig ourselves
|
|
const ParseKubeConfig Property = "parse_kubeconfig"
|
|
|
|
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"`
|
|
}
|
|
|
|
type KubeConfig struct {
|
|
CurrentContext string `yaml:"current-context"`
|
|
Contexts []struct {
|
|
Context KubeConfigContext `yaml:"context"`
|
|
Name string `yaml:"name"`
|
|
} `yaml:"contexts"`
|
|
}
|
|
|
|
func (k *kubectl) string() string {
|
|
segmentTemplate := k.props.getString(SegmentTemplate, "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}")
|
|
template := &textTemplate{
|
|
Template: segmentTemplate,
|
|
Context: k,
|
|
Env: k.env,
|
|
}
|
|
text, err := template.render()
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return text
|
|
}
|
|
|
|
func (k *kubectl) init(props properties, env environmentInfo) {
|
|
k.props = props
|
|
k.env = env
|
|
}
|
|
|
|
func (k *kubectl) enabled() bool {
|
|
parseKubeConfig := k.props.getBool(ParseKubeConfig, false)
|
|
if parseKubeConfig {
|
|
return k.doParseKubeConfig()
|
|
}
|
|
return k.doCallKubectl()
|
|
}
|
|
|
|
func (k *kubectl) doParseKubeConfig() bool {
|
|
// Follow kubectl search rules (see https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable)
|
|
// TL;DR: KUBECONFIG can contain a list of files. If it's empty ~/.kube/config is used. First file in list wins when merging keys.
|
|
kubeconfigs := filepath.SplitList(k.env.getenv("KUBECONFIG"))
|
|
if len(kubeconfigs) == 0 {
|
|
kubeconfigs = []string{filepath.Join(k.env.homeDir(), ".kube/config")}
|
|
}
|
|
contexts := make(map[string]KubeConfigContext)
|
|
k.Context = ""
|
|
for _, kubeconfig := range kubeconfigs {
|
|
if len(kubeconfig) == 0 {
|
|
continue
|
|
}
|
|
|
|
content := k.env.getFileContent(kubeconfig)
|
|
|
|
var config KubeConfig
|
|
err := yaml.Unmarshal([]byte(content), &config)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, context := range config.Contexts {
|
|
if _, exists := contexts[context.Name]; !exists {
|
|
contexts[context.Name] = context.Context
|
|
}
|
|
}
|
|
|
|
if len(k.Context) == 0 {
|
|
k.Context = config.CurrentContext
|
|
}
|
|
|
|
context, exists := contexts[k.Context]
|
|
if exists {
|
|
k.KubeConfigContext = context
|
|
return true
|
|
}
|
|
}
|
|
|
|
displayError := k.props.getBool(DisplayError, false)
|
|
if !displayError {
|
|
return false
|
|
}
|
|
k.setError("KUBECONFIG ERR")
|
|
return true
|
|
}
|
|
|
|
func (k *kubectl) doCallKubectl() bool {
|
|
cmd := "kubectl"
|
|
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}")
|
|
displayError := k.props.getBool(DisplayError, false)
|
|
if err != nil && displayError {
|
|
k.setError("KUBECTL ERR")
|
|
return true
|
|
}
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
values := strings.Split(result, ",")
|
|
k.Context = values[0]
|
|
k.Namespace = values[1]
|
|
k.User = values[2]
|
|
k.Cluster = values[3]
|
|
return len(k.Context) > 0
|
|
}
|
|
|
|
func (k *kubectl) setError(message string) {
|
|
if len(k.Context) == 0 {
|
|
k.Context = message
|
|
}
|
|
k.Namespace = message
|
|
k.User = message
|
|
k.Cluster = message
|
|
}
|