feat(kubectl): add cache to kubectl

This commit is contained in:
Elad Leev 2024-07-18 21:30:18 +01:00 committed by Jan De Dobbeleer
parent 7a6478269c
commit 576ec376d5
3 changed files with 66 additions and 0 deletions

View file

@ -1,6 +1,8 @@
package segments
import (
"encoding/json"
"fmt"
"path/filepath"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
@ -13,12 +15,15 @@ import (
const (
ParseKubeConfig properties.Property = "parse_kubeconfig"
ContextAliases properties.Property = "context_aliases"
kubectlCacheKey = "kubectl"
)
type Kubectl struct {
props properties.Properties
env runtime.Environment
dirty bool
Context string
KubeContext
@ -47,11 +52,50 @@ func (k *Kubectl) Init(props properties.Properties, env runtime.Environment) {
k.env = env
}
func (k *Kubectl) setCacheValue(timeout int) {
if !k.dirty {
return
}
cachedData, _ := json.Marshal(k)
k.env.Cache().Set(kubectlCacheKey, string(cachedData), timeout)
}
func (k *Kubectl) restoreCacheValue() error {
if val, found := k.env.Cache().Get(kubectlCacheKey); found {
err := json.Unmarshal([]byte(val), k)
if err != nil {
k.env.Error(err)
return err
}
return nil
}
return fmt.Errorf("no data in cache")
}
func (k *Kubectl) Enabled() bool {
cacheTimeout := k.props.GetInt(properties.CacheTimeout, 1)
if cacheTimeout > 0 {
if err := k.restoreCacheValue(); err == nil {
return true
}
}
defer func() {
if cacheTimeout > 0 {
k.setCacheValue(cacheTimeout)
}
}()
parseKubeConfig := k.props.GetBool(ParseKubeConfig, false)
if parseKubeConfig {
return k.doParseKubeConfig()
}
return k.doCallKubectl()
}
@ -62,8 +106,10 @@ func (k *Kubectl) doParseKubeConfig() bool {
if len(kubeconfigs) == 0 {
kubeconfigs = []string{filepath.Join(k.env.Home(), ".kube/config")}
}
contexts := make(map[string]*KubeContext)
k.Context = ""
for _, kubeconfig := range kubeconfigs {
if len(kubeconfig) == 0 {
continue
@ -97,6 +143,7 @@ func (k *Kubectl) doParseKubeConfig() bool {
}
k.SetContextAlias()
k.dirty = true
return true
}
@ -114,12 +161,14 @@ func (k *Kubectl) doCallKubectl() bool {
if !k.env.HasCommand(cmd) {
return false
}
result, err := k.env.RunCommand(cmd, "config", "view", "--output", "yaml", "--minify")
displayError := k.props.GetBool(properties.DisplayError, false)
if err != nil && displayError {
k.setError("KUBECTL ERR")
return true
}
if err != nil {
return false
}
@ -129,11 +178,15 @@ func (k *Kubectl) doCallKubectl() bool {
if err != nil {
return false
}
k.Context = config.CurrentContext
k.SetContextAlias()
k.dirty = true
if len(config.Contexts) > 0 {
k.KubeContext = *config.Contexts[0].Context
}
return true
}
@ -141,6 +194,7 @@ func (k *Kubectl) setError(message string) {
if len(k.Context) == 0 {
k.Context = message
}
k.Namespace = message
k.User = message
k.Cluster = message

View file

@ -134,11 +134,13 @@ func TestKubectlSegment(t *testing.T) {
for _, tc := range cases {
env := new(mock.Environment)
env.On("HasCommand", "kubectl").Return(tc.KubectlExists)
var kubeconfig string
content, err := os.ReadFile("../test/kubectl.yml")
if err == nil {
kubeconfig = fmt.Sprintf(string(content), tc.Cluster, tc.UserName, tc.Namespace, tc.Context)
}
var kubectlErr error
if tc.KubectlErr {
kubectlErr = &runtime.CommandError{
@ -146,11 +148,14 @@ func TestKubectlSegment(t *testing.T) {
ExitCode: 1,
}
}
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("FileContent", path).Return(content)
}
env.On("Home").Return("testhome")
k := &Kubectl{
@ -159,8 +164,10 @@ func TestKubectlSegment(t *testing.T) {
properties.DisplayError: tc.DisplayError,
ParseKubeConfig: tc.ParseKubeConfig,
ContextAliases: tc.ContextAliases,
properties.CacheTimeout: 0,
},
}
assert.Equal(t, tc.ExpectedEnabled, k.Enabled(), tc.Case)
if tc.ExpectedEnabled {
assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, k), tc.Case)

View file

@ -8,6 +8,10 @@ sidebar_label: Kubectl
Display the currently active Kubernetes context name and namespace name.
:::caution
The Kubernetes context is cached for 1 minute by default. To avoid caching, set `cache_timeout` to 0.
:::
## Sample Configuration
import Config from "@site/src/components/Config.js";
@ -35,6 +39,7 @@ import Config from "@site/src/components/Config.js";
| `display_error` | `boolean` | `false` | show the error context when failing to retrieve the kubectl information |
| `parse_kubeconfig` | `boolean` | `false` | parse kubeconfig files instead of calling out to kubectl to improve performance |
| `context_aliases` | `object` | | custom context namespace |
| `cache_timeout` | `int` | `1` | in minutes - how long is the context cached |
## Template ([info][templates])