parent
c0786c2016
commit
e2f1626764
|
|
@ -215,30 +215,32 @@ func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
|||
}
|
||||
|
||||
// CheckConnectivity return true if api server is cool or false otherwise.
|
||||
func (a *APIClient) CheckConnectivity() (ok bool) {
|
||||
func (a *APIClient) CheckConnectivity() bool {
|
||||
a.mx.Lock()
|
||||
defer a.mx.Unlock()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
a.connOK = false
|
||||
}
|
||||
if !ok {
|
||||
if !a.connOK {
|
||||
a.clearCache()
|
||||
}
|
||||
a.connOK = ok
|
||||
}()
|
||||
|
||||
// Need to reload to pickup any kubeconfig changes.
|
||||
cfg, err := NewConfig(a.config.flags).RESTConfig()
|
||||
if err != nil {
|
||||
return false
|
||||
log.Error().Err(err).Msgf("restConfig load failed")
|
||||
a.connOK = false
|
||||
return a.connOK
|
||||
}
|
||||
cfg.Timeout = checkConnTimeout
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to connect to api server")
|
||||
return
|
||||
a.connOK = false
|
||||
return a.connOK
|
||||
}
|
||||
|
||||
// Check connection
|
||||
|
|
@ -247,12 +249,13 @@ func (a *APIClient) CheckConnectivity() (ok bool) {
|
|||
log.Debug().Msgf("RESETING CON!!")
|
||||
a.reset()
|
||||
}
|
||||
ok = true
|
||||
a.connOK = true
|
||||
} else {
|
||||
log.Error().Err(err).Msgf("K9s can't connect to cluster")
|
||||
a.connOK = false
|
||||
}
|
||||
|
||||
return
|
||||
return a.connOK
|
||||
}
|
||||
|
||||
// Config return a kubernetes configuration.
|
||||
|
|
@ -385,6 +388,9 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
|||
|
||||
// SwitchContext handles kubeconfig context switches.
|
||||
func (a *APIClient) SwitchContext(name string) error {
|
||||
a.mx.Lock()
|
||||
defer a.mx.Unlock()
|
||||
|
||||
log.Debug().Msgf("Switching context %q", name)
|
||||
currentCtx, err := a.config.CurrentContextName()
|
||||
if err != nil {
|
||||
|
|
@ -411,9 +417,6 @@ func (a *APIClient) SwitchContext(name string) error {
|
|||
}
|
||||
|
||||
func (a *APIClient) reset() {
|
||||
a.mx.Lock()
|
||||
defer a.mx.Unlock()
|
||||
|
||||
a.config.reset()
|
||||
a.cache = cache.NewLRUExpireCache(cacheSize)
|
||||
a.client, a.dClient, a.nsClient, a.mxsClient = nil, nil, nil, nil
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
// K9sAlias manages K9s aliases.
|
||||
var K9sAlias = filepath.Join(K9sHome, "alias.yml")
|
||||
var K9sAlias = filepath.Join(K9sHome(), "alias.yml")
|
||||
|
||||
// Alias tracks shortname to GVR mappings.
|
||||
type Alias map[string]string
|
||||
|
|
|
|||
|
|
@ -14,11 +14,14 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
// K9sConfig represents K9s configuration dir env var.
|
||||
const K9sConfig = "K9SCONFIG"
|
||||
|
||||
var (
|
||||
// K9sHome represent K9s home directory.
|
||||
K9sHome = filepath.Join(mustK9sHome(), ".k9s")
|
||||
// DefaultK9sHome represent K9s home directory.
|
||||
DefaultK9sHome = filepath.Join(mustK9sHome(), ".k9s")
|
||||
// K9sConfigFile represents K9s config file location.
|
||||
K9sConfigFile = filepath.Join(K9sHome, "config.yml")
|
||||
K9sConfigFile = filepath.Join(K9sHome(), "config.yml")
|
||||
// K9sLogs represents K9s log.
|
||||
K9sLogs = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", MustK9sUser()))
|
||||
// K9sDumpDir represents a directory where K9s screen dumps will be persisted.
|
||||
|
|
@ -53,6 +56,15 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
// K9sHome returns k9s configs home directory.
|
||||
func K9sHome() string {
|
||||
if env := os.Getenv(K9sConfig); env != "" {
|
||||
return env
|
||||
}
|
||||
|
||||
return DefaultK9sHome
|
||||
}
|
||||
|
||||
// NewConfig creates a new default config.
|
||||
func NewConfig(ks KubeSettings) *Config {
|
||||
return &Config{K9s: NewK9s(), settings: ks}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func InNSList(nn []interface{}, ns string) bool {
|
|||
func mustK9sHome() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Die on retriving user home")
|
||||
log.Fatal().Err(err).Msg("Die on retrieving user home")
|
||||
}
|
||||
return usr.HomeDir
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ func mustK9sHome() string {
|
|||
func MustK9sUser() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Die on retriving user info")
|
||||
log.Fatal().Err(err).Msg("Die on retrieving user info")
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// K9sHotKeys manages K9s hotKeys.
|
||||
var K9sHotKeys = filepath.Join(K9sHome, "hotkey.yml")
|
||||
var K9sHotKeys = filepath.Join(K9sHome(), "hotkey.yml")
|
||||
|
||||
// HotKeys represents a collection of plugins.
|
||||
type HotKeys struct {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// K9sPlugins manages K9s plugins.
|
||||
var K9sPlugins = filepath.Join(K9sHome, "plugin.yml")
|
||||
var K9sPlugins = filepath.Join(K9sHome(), "plugin.yml")
|
||||
|
||||
// Plugins represents a collection of plugins.
|
||||
type Plugins struct {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
// K9sStylesFile represents K9s skins file location.
|
||||
var K9sStylesFile = filepath.Join(K9sHome, "skin.yml")
|
||||
var K9sStylesFile = filepath.Join(K9sHome(), "skin.yml")
|
||||
|
||||
// StyleListener represents a skin's listener.
|
||||
type StyleListener interface {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// K9sViewConfigFile represents the location for the views configuration.
|
||||
var K9sViewConfigFile = filepath.Join(K9sHome, "views.yml")
|
||||
var K9sViewConfigFile = filepath.Join(K9sHome(), "views.yml")
|
||||
|
||||
// ViewConfigListener represents a view config listener.
|
||||
type ViewConfigListener interface {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
|
|||
}
|
||||
wait, ok := ctx.Value(internal.KeyWait).(bool)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Wait")
|
||||
log.Error().Msgf("expecting Context Wait Key")
|
||||
}
|
||||
|
||||
ss := scanners()
|
||||
|
|
@ -105,7 +105,7 @@ func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
|
|||
// ScanForSARefs scans cluster resources for serviceaccount refs.
|
||||
func ScanForSARefs(ctx context.Context, f Factory) (Refs, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("Cluster Scan %v", time.Since(t))
|
||||
log.Debug().Msgf("SA Cluster Scan %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
|
|
|
|||
|
|
@ -197,12 +197,29 @@ func (d *Deployment) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs
|
|||
GVR: d.GVR(),
|
||||
FQN: client.FQN(dp.Namespace, dp.Name),
|
||||
})
|
||||
case "v1/persistentvolumeclaims":
|
||||
if !hasPVC(&dp.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(dp.Namespace, dp.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func hasPVC(spec *v1.PodSpec, name string) bool {
|
||||
for _, v := range spec.Volumes {
|
||||
if v.PersistentVolumeClaim != nil && v.PersistentVolumeClaim.ClaimName == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasConfigMap(spec *v1.PodSpec, name string) bool {
|
||||
for _, c := range spec.InitContainers {
|
||||
if containerHasConfigMap(c, name) {
|
||||
|
|
|
|||
|
|
@ -211,6 +211,14 @@ func (d *DaemonSet) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs,
|
|||
GVR: d.GVR(),
|
||||
FQN: client.FQN(ds.Namespace, ds.Name),
|
||||
})
|
||||
case "v1/persistentvolumeclaims":
|
||||
if !hasPVC(&ds.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(ds.Namespace, ds.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -301,6 +301,14 @@ func (p *Pod) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error
|
|||
GVR: p.GVR(),
|
||||
FQN: client.FQN(pod.Namespace, pod.Name),
|
||||
})
|
||||
case "v1/persistentvolumeclaims":
|
||||
if !hasPVC(&pod.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: p.GVR(),
|
||||
FQN: client.FQN(pod.Namespace, pod.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,17 +55,16 @@ func (p *Popeye) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
|||
}
|
||||
}(time.Now())
|
||||
|
||||
flags := config.NewFlags()
|
||||
js := "json"
|
||||
flags, js := config.NewFlags(), "json"
|
||||
flags.Output = &js
|
||||
|
||||
if report, ok := ctx.Value(internal.KeyPath).(string); ok && report != "" {
|
||||
sections := []string{report}
|
||||
flags.Sections = §ions
|
||||
}
|
||||
spinach := filepath.Join(cfg.K9sHome, "spinach.yml")
|
||||
spinach := filepath.Join(cfg.K9sHome(), "spinach.yml")
|
||||
if c, err := p.Factory.Client().Config().CurrentContextName(); err == nil {
|
||||
spinach = filepath.Join(cfg.K9sHome, fmt.Sprintf("%s_spinach.yml", c))
|
||||
spinach = filepath.Join(cfg.K9sHome(), fmt.Sprintf("%s_spinach.yml", c))
|
||||
}
|
||||
if _, err := os.Stat(spinach); err == nil {
|
||||
flags.Spinach = &spinach
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
@ -196,6 +197,23 @@ func (s *StatefulSet) Scan(ctx context.Context, gvr, fqn string, wait bool) (Ref
|
|||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
case "v1/persistentvolumeclaims":
|
||||
for _, v := range sts.Spec.VolumeClaimTemplates {
|
||||
if !strings.HasPrefix(n, v.Name) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
}
|
||||
if !hasPVC(&sts.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,14 +117,14 @@ func (c *Configurator) StylesWatcher(ctx context.Context, s synchronizer) error
|
|||
|
||||
// BenchConfig location of the benchmarks configuration file.
|
||||
func BenchConfig(context string) string {
|
||||
return filepath.Join(config.K9sHome, config.K9sBench+"-"+context+".yml")
|
||||
return filepath.Join(config.K9sHome(), config.K9sBench+"-"+context+".yml")
|
||||
}
|
||||
|
||||
// RefreshStyles load for skin configuration changes.
|
||||
func (c *Configurator) RefreshStyles(context string) {
|
||||
c.BenchFile = BenchConfig(context)
|
||||
|
||||
clusterSkins := filepath.Join(config.K9sHome, fmt.Sprintf("%s_skin.yml", context))
|
||||
clusterSkins := filepath.Join(config.K9sHome(), fmt.Sprintf("%s_skin.yml", context))
|
||||
if c.Styles == nil {
|
||||
c.Styles = config.NewStyles()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package ui_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
|
|
@ -12,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func TestBenchConfig(t *testing.T) {
|
||||
config.K9sHome = "/tmp/blee"
|
||||
os.Setenv(config.K9sConfig, "/tmp/blee")
|
||||
assert.Equal(t, "/tmp/blee/bench-fred.yml", ui.BenchConfig("fred"))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func TestAliasNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, v.Init(makeContext()))
|
||||
assert.Equal(t, "Aliases", v.Name())
|
||||
assert.Equal(t, 5, len(v.Hints()))
|
||||
assert.Equal(t, 6, len(v.Hints()))
|
||||
}
|
||||
|
||||
func TestAliasSearch(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@ func (a *App) refreshCluster() {
|
|||
if c != nil {
|
||||
c.Start()
|
||||
}
|
||||
} else {
|
||||
a.ClearStatus(true)
|
||||
}
|
||||
} else {
|
||||
atomic.AddInt32(&a.conRetry, 1)
|
||||
|
|
@ -363,6 +365,11 @@ func (a *App) switchCtx(name string, loadPods bool) error {
|
|||
if err := a.command.Reset(true); err != nil {
|
||||
return err
|
||||
}
|
||||
v := a.Config.ActiveView()
|
||||
if v == "" || isContextCmd(v) || loadPods {
|
||||
v = "pod"
|
||||
a.Config.SetActiveView(v)
|
||||
}
|
||||
if err := a.Config.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("Config save failed!")
|
||||
}
|
||||
|
|
@ -370,10 +377,6 @@ func (a *App) switchCtx(name string, loadPods bool) error {
|
|||
|
||||
a.Flash().Infof("Switching context to %s", name)
|
||||
a.ReloadStyles(name)
|
||||
v := a.Config.ActiveView()
|
||||
if v == "" || v == "ctx" || v == "context" {
|
||||
v = "pod"
|
||||
}
|
||||
if err := a.gotoResource(v, "", true); loadPods && err != nil {
|
||||
a.Flash().Err(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestConfigMapNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "ConfigMaps", s.Name())
|
||||
assert.Equal(t, 5, len(s.Hints()))
|
||||
assert.Equal(t, 6, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,17 +151,21 @@ func (c *Command) defaultCmd() error {
|
|||
tokens := strings.Split(view, " ")
|
||||
cmd := view
|
||||
ns, err := c.app.Conn().Config().CurrentNamespaceName()
|
||||
if err == nil {
|
||||
if err == nil && !isContextCmd(tokens[0]) {
|
||||
cmd = tokens[0] + " " + ns
|
||||
}
|
||||
|
||||
if err := c.run(cmd, "", true); err != nil {
|
||||
log.Error().Err(err).Msgf("Saved command load failed. Loading default view")
|
||||
log.Error().Err(err).Msgf("Default run command failed")
|
||||
return c.run("meow", err.Error(), true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isContextCmd(c string) bool {
|
||||
return c == "ctx" || c == "context"
|
||||
}
|
||||
|
||||
func (c *Command) specialCmd(cmd, path string) bool {
|
||||
cmds := strings.Split(cmd, " ")
|
||||
switch cmds[0] {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestContainerNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, c.Init(makeCtx()))
|
||||
assert.Equal(t, "Containers", c.Name())
|
||||
assert.Equal(t, 16, len(c.Hints()))
|
||||
assert.Equal(t, 17, len(c.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestContext(t *testing.T) {
|
|||
|
||||
assert.Nil(t, ctx.Init(makeCtx()))
|
||||
assert.Equal(t, "Contexts", ctx.Name())
|
||||
assert.Equal(t, 3, len(ctx.Hints()))
|
||||
assert.Equal(t, 4, len(ctx.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestDeploy(t *testing.T) {
|
|||
|
||||
assert.Nil(t, v.Init(makeCtx()))
|
||||
assert.Equal(t, "Deployments", v.Name())
|
||||
assert.Equal(t, 11, len(v.Hints()))
|
||||
assert.Equal(t, 12, len(v.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestDaemonSet(t *testing.T) {
|
|||
|
||||
assert.Nil(t, v.Init(makeCtx()))
|
||||
assert.Equal(t, "DaemonSets", v.Name())
|
||||
assert.Equal(t, 12, len(v.Hints()))
|
||||
assert.Equal(t, 13, len(v.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func (h *Help) Init(ctx context.Context) error {
|
|||
func (h *Help) bindKeys() {
|
||||
h.Actions().Delete(ui.KeySpace, tcell.KeyCtrlSpace, tcell.KeyCtrlS)
|
||||
h.Actions().Set(ui.KeyActions{
|
||||
tcell.KeyEscape: ui.NewKeyAction("Back", h.app.PrevCmd, false),
|
||||
tcell.KeyEscape: ui.NewKeyAction("Back", h.app.PrevCmd, true),
|
||||
ui.KeyHelp: ui.NewKeyAction("Back", h.app.PrevCmd, false),
|
||||
tcell.KeyEnter: ui.NewKeyAction("Back", h.app.PrevCmd, false),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func TestHelp(t *testing.T) {
|
|||
v := view.NewHelp()
|
||||
|
||||
assert.Nil(t, v.Init(ctx))
|
||||
assert.Equal(t, 22, v.GetRowCount())
|
||||
assert.Equal(t, 23, v.GetRowCount())
|
||||
assert.Equal(t, 8, v.GetColumnCount())
|
||||
assert.Equal(t, "<a>", strings.TrimSpace(v.GetCell(1, 0).Text))
|
||||
assert.Equal(t, "Attach", strings.TrimSpace(v.GetCell(1, 1).Text))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ func NewJob(gvr client.GVR) ResourceViewer {
|
|||
j := Job{ResourceViewer: NewLogsExtender(NewBrowser(gvr), nil)}
|
||||
j.GetTable().SetEnterFn(j.showPods)
|
||||
j.GetTable().SetColorerFn(render.Job{}.ColorerFunc())
|
||||
j.GetTable().SetSortCol("AGE", true)
|
||||
|
||||
return &j
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestNSCleanser(t *testing.T) {
|
|||
|
||||
assert.Nil(t, ns.Init(makeCtx()))
|
||||
assert.Equal(t, "Namespaces", ns.Name())
|
||||
assert.Equal(t, 5, len(ns.Hints()))
|
||||
assert.Equal(t, 6, len(ns.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestPortForwardNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, pf.Init(makeCtx()))
|
||||
assert.Equal(t, "PortForwards", pf.Name())
|
||||
assert.Equal(t, 9, len(pf.Hints()))
|
||||
assert.Equal(t, 10, len(pf.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func TestPodNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, po.Init(makeCtx()))
|
||||
assert.Equal(t, "Pods", po.Name())
|
||||
assert.Equal(t, 21, len(po.Hints()))
|
||||
assert.Equal(t, 22, len(po.Hints()))
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// PersistentVolumeClaim represents a PVC custom viewer.
|
||||
|
|
@ -13,20 +14,25 @@ type PersistentVolumeClaim struct {
|
|||
|
||||
// NewPersistentVolumeClaim returns a new viewer.
|
||||
func NewPersistentVolumeClaim(gvr client.GVR) ResourceViewer {
|
||||
d := PersistentVolumeClaim{
|
||||
v := PersistentVolumeClaim{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
d.SetBindKeysFn(d.bindKeys)
|
||||
d.GetTable().SetColorerFn(render.PersistentVolumeClaim{}.ColorerFunc())
|
||||
v.SetBindKeysFn(v.bindKeys)
|
||||
v.GetTable().SetColorerFn(render.PersistentVolumeClaim{}.ColorerFunc())
|
||||
|
||||
return &d
|
||||
return &v
|
||||
}
|
||||
|
||||
func (d *PersistentVolumeClaim) bindKeys(aa ui.KeyActions) {
|
||||
func (p *PersistentVolumeClaim) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyShiftS: ui.NewKeyAction("Sort Status", d.GetTable().SortColCmd("STATUS", true), false),
|
||||
ui.KeyShiftV: ui.NewKeyAction("Sort Volume", d.GetTable().SortColCmd("VOLUME", true), false),
|
||||
ui.KeyShiftO: ui.NewKeyAction("Sort StorageClass", d.GetTable().SortColCmd("STORAGECLASS", true), false),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort Capacity", d.GetTable().SortColCmd("CAPACITY", true), false),
|
||||
ui.KeyU: ui.NewKeyAction("UsedBy", p.refCmd, true),
|
||||
ui.KeyShiftS: ui.NewKeyAction("Sort Status", p.GetTable().SortColCmd("STATUS", true), false),
|
||||
ui.KeyShiftV: ui.NewKeyAction("Sort Volume", p.GetTable().SortColCmd("VOLUME", true), false),
|
||||
ui.KeyShiftO: ui.NewKeyAction("Sort StorageClass", p.GetTable().SortColCmd("STORAGECLASS", true), false),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort Capacity", p.GetTable().SortColCmd("CAPACITY", true), false),
|
||||
})
|
||||
}
|
||||
|
||||
func (p *PersistentVolumeClaim) refCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
return scanRefs(evt, p.App(), p.GetTable(), "v1/persistentvolumeclaims")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package view_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/view"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPVCNew(t *testing.T) {
|
||||
v := view.NewPersistentVolumeClaim(client.NewGVR("v1/persistentvolumeclaims"))
|
||||
|
||||
assert.Nil(t, v.Init(makeCtx()))
|
||||
assert.Equal(t, "PersistentVolumeClaims", v.Name())
|
||||
assert.Equal(t, 10, len(v.Hints()))
|
||||
}
|
||||
|
|
@ -13,5 +13,5 @@ func TestRbacNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, v.Init(makeCtx()))
|
||||
assert.Equal(t, "Rbac", v.Name())
|
||||
assert.Equal(t, 4, len(v.Hints()))
|
||||
assert.Equal(t, 5, len(v.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestReferenceNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "References", s.Name())
|
||||
assert.Equal(t, 3, len(s.Hints()))
|
||||
assert.Equal(t, 4, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestScreenDumpNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, po.Init(makeCtx()))
|
||||
assert.Equal(t, "ScreenDumps", po.Name())
|
||||
assert.Equal(t, 4, len(po.Hints()))
|
||||
assert.Equal(t, 5, len(po.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestSecretNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "Secrets", s.Name())
|
||||
assert.Equal(t, 6, len(s.Hints()))
|
||||
assert.Equal(t, 7, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestStatefulSetNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "StatefulSets", s.Name())
|
||||
assert.Equal(t, 10, len(s.Hints()))
|
||||
assert.Equal(t, 11, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,14 @@ func init() {
|
|||
Verbs: []string{"get", "list", "watch", "delete"},
|
||||
Categories: []string{"k9s"},
|
||||
})
|
||||
dao.MetaAccess.RegisterMeta("v1/persistentvolumeclaims", metav1.APIResource{
|
||||
Name: "persistentvolumeclaims",
|
||||
SingularName: "persistentvolumeclaim",
|
||||
Namespaced: true,
|
||||
Kind: "PersistentVolumeClaims",
|
||||
Verbs: []string{"get", "list", "watch", "delete"},
|
||||
Categories: []string{"k9s"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestServiceNew(t *testing.T) {
|
||||
|
|
@ -148,5 +156,5 @@ func TestServiceNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "Services", s.Name())
|
||||
assert.Equal(t, 9, len(s.Hints()))
|
||||
assert.Equal(t, 10, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ func (t *Table) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
func (t *Table) bindKeys() {
|
||||
t.Actions().Add(ui.KeyActions{
|
||||
ui.KeyHelp: ui.NewKeyAction("Help", t.App().helpCmd, true),
|
||||
ui.KeySpace: ui.NewSharedKeyAction("Mark", t.markCmd, false),
|
||||
tcell.KeyCtrlSpace: ui.NewSharedKeyAction("Mark Range", t.markSpanCmd, false),
|
||||
tcell.KeyCtrlBackslash: ui.NewSharedKeyAction("Marks Clear", t.clearMarksCmd, false),
|
||||
|
|
|
|||
Loading…
Reference in New Issue