mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-01-29 12:01:07 -08:00
feat(kubectl): add cache to kubectl
This commit is contained in:
parent
7a6478269c
commit
576ec376d5
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
Loading…
Reference in a new issue