From 037d6d3f5480a6d698d3f92240f457ee8bfc8153 Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 14 May 2020 18:27:02 -0600 Subject: [PATCH 1/6] update deps - fix ns issues --- Dockerfile | 2 +- cmd/root.go | 1 - go.mod | 2 +- go.sum | 2 ++ internal/config/config.go | 17 +++++------------ internal/view/app.go | 28 ++++++++++++++++++++-------- internal/view/command.go | 13 ++++++++++++- internal/view/container.go | 11 +++-------- internal/view/exec.go | 12 ++++++++---- internal/view/pod.go | 28 +++++++++++++++++----------- internal/view/popeye.go | 9 +++++++-- internal/view/pvc.go | 32 ++++++++++++++++++++++++++++++++ internal/view/registrar.go | 3 +++ internal/watch/factory.go | 7 ------- main.go | 2 ++ 15 files changed, 113 insertions(+), 56 deletions(-) create mode 100644 internal/view/pvc.go diff --git a/Dockerfile b/Dockerfile index fa8c4186..4342f7dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ FROM alpine:3.10.0 COPY --from=build /k9s/execs/k9s /bin/k9s ENV KUBE_LATEST_VERSION="v1.18.1" RUN apk add --update ca-certificates \ - && apk add --update -t deps curl \ + && apk add --update -t deps curl vim \ && curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl \ && chmod +x /usr/local/bin/kubectl \ && apk del --purge deps \ diff --git a/cmd/root.go b/cmd/root.go index 6b5794ff..420aa137 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -105,7 +105,6 @@ func loadConfiguration() *config.Config { log.Warn().Msg("Unable to locate K9s config. Generating new configuration...") } - log.Debug().Msgf("DEMO MODE %#v", demoMode) if demoMode != nil { k9sCfg.SetDemoMode(*demoMode) } diff --git a/go.mod b/go.mod index e9961927..d6354609 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/atotto/clipboard v0.1.2 - github.com/derailed/popeye v0.8.1 + github.com/derailed/popeye v0.8.2 github.com/derailed/tview v0.3.10 github.com/drone/envsubst v1.0.2 // indirect github.com/fatih/color v1.9.0 diff --git a/go.sum b/go.sum index 144800ea..93d1eeec 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xb github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/derailed/popeye v0.8.1 h1:N69XH0NZTBkrNj8qvUzy6Z6bP7+jx0AwollETqvc3dc= github.com/derailed/popeye v0.8.1/go.mod h1:OBHcJDa50VpE9QNyOU243bNOtHb29MyLlVHJolwlwas= +github.com/derailed/popeye v0.8.2 h1:O4JcIC3MwJS9pNKb8ZHLUJKoLPZW0gfcMqFoSYYdbAs= +github.com/derailed/popeye v0.8.2/go.mod h1:i4ge2tKHDKXgUq3NzFlIhVIBNHS0zFDMJWXsC2bVe2A= github.com/derailed/tview v0.3.10 h1:n+iQwYh9Ff9STdR5hBhp+rTJRlu59q2xP2pHvwQbYPw= github.com/derailed/tview v0.3.10/go.mod h1:GJ3k/TIzEE+sj1L09/usk6HrkjsdadSsb03eHgPbcII= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= diff --git a/internal/config/config.go b/internal/config/config.go index ed4bc780..f7095839 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -84,13 +84,13 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags) error { if c.K9s.CurrentContext == "" { return errors.New("Invalid kubeconfig context detected") } - ctx, ok := cfg.Contexts[c.K9s.CurrentContext] + context, ok := cfg.Contexts[c.K9s.CurrentContext] if !ok { return fmt.Errorf("The specified context %q does not exists in kubeconfig", c.K9s.CurrentContext) } - c.K9s.CurrentCluster = ctx.Cluster - if len(ctx.Namespace) != 0 { - if err := c.SetActiveNamespace(ctx.Namespace); err != nil { + c.K9s.CurrentCluster = context.Cluster + if len(context.Namespace) != 0 { + if err := c.SetActiveNamespace(context.Namespace); err != nil { return err } } @@ -124,13 +124,6 @@ func (c *Config) CurrentCluster() *Cluster { // ActiveNamespace returns the active namespace in the current cluster. func (c *Config) ActiveNamespace() string { - if c.client != nil { - ns := c.client.ActiveNamespace() - if client.IsNamespaced(ns) { - return ns - } - } - if cl := c.CurrentCluster(); cl != nil { if cl.Namespace != nil { return cl.Namespace.Active @@ -162,9 +155,9 @@ func (c *Config) SetActiveNamespace(ns string) error { if c.K9s.ActiveCluster() != nil { return c.K9s.ActiveCluster().Namespace.SetActive(ns, c.settings) } - err := errors.New("no active cluster. unable to set active namespace") log.Error().Err(err).Msg("SetActiveNamespace") + return err } diff --git a/internal/view/app.go b/internal/view/app.go index 5561f1a0..c7f83590 100644 --- a/internal/view/app.go +++ b/internal/view/app.go @@ -88,8 +88,13 @@ func (a *App) Init(version string, rate int) error { return errors.New("No client connection detected") } ns, err := a.Conn().Config().CurrentNamespaceName() + log.Debug().Msgf("CURRENT-NS %q -- %v", ns, err) if err != nil { - log.Info().Msg("No namespace specified using all namespaces") + log.Info().Msg("No namespace specified using cluster default namespace") + } else { + if err := a.Config.SetActiveNamespace(ns); err != nil { + log.Error().Err(err).Msgf("Fail to set active namespace to %q", ns) + } } a.factory = watch.NewFactory(a.Conn()) @@ -111,6 +116,13 @@ func (a *App) Init(version string, rate int) error { a.CmdBuff().SetSuggestionFn(a.suggestCommand()) a.CmdBuff().AddListener(a) + a.layout(ctx, version) + a.initSignals() + + return nil +} + +func (a *App) layout(ctx context.Context, version string) { flash := ui.NewFlash(a.App) go flash.Watch(ctx, a.Flash().Channel()) @@ -123,10 +135,6 @@ func (a *App) Init(version string, rate int) error { a.Main.AddPage("main", main, true, false) a.Main.AddPage("splash", ui.NewSplash(a.Styles, version), true, true) a.toggleHeader(!a.Config.K9s.GetHeadless()) - - a.initSignals() - - return nil } func (a *App) initSignals() { @@ -134,8 +142,12 @@ func (a *App) initSignals() { signal.Notify(sig, syscall.SIGABRT, syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT) go func(sig chan os.Signal) { - <-sig - nukeK9sShell(a.Conn()) + signal := <-sig + if signal == syscall.SIGHUP { + a.BailOut() + return + } + nukeK9sShell(a) }(sig) } @@ -375,7 +387,7 @@ func (a *App) BailOut() { } }() - nukeK9sShell(a.Conn()) + nukeK9sShell(a) a.factory.Terminate() a.App.BailOut() } diff --git a/internal/view/command.go b/internal/view/command.go index db3fac26..fdeeb571 100644 --- a/internal/view/command.go +++ b/internal/view/command.go @@ -158,7 +158,18 @@ func (c *Command) run(cmd, path string, clearStack bool) error { } func (c *Command) defaultCmd() error { - if err := c.run(c.app.Config.ActiveView(), "", true); err != nil { + view := c.app.Config.ActiveView() + if view == "" { + return c.run("pod", "", true) + } + tokens := strings.Split(view, " ") + cmd := view + ns, err := c.app.Conn().Config().CurrentNamespaceName() + if err == nil { + cmd = tokens[0] + " " + ns + } + + if err := c.run(cmd, "", true); err != nil { log.Error().Err(err).Msgf("Saved command load failed. Loading default view") return c.run("pod", "", true) } diff --git a/internal/view/container.go b/internal/view/container.go index 2f2993cb..d1b51e48 100644 --- a/internal/view/container.go +++ b/internal/view/container.go @@ -50,15 +50,10 @@ func (c *Container) bindKeys(aa ui.KeyActions) { } aa.Add(ui.KeyActions{ - ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true), - ui.KeyShiftT: ui.NewKeyAction("Sort Restart", c.GetTable().SortColCmd("RESTARTS", false), false), - ui.KeyShiftC: ui.NewKeyAction("Sort CPU", c.GetTable().SortColCmd(cpuCol, false), false), - ui.KeyShiftM: ui.NewKeyAction("Sort MEM", c.GetTable().SortColCmd(memCol, false), false), - ui.KeyShiftX: ui.NewKeyAction("Sort %CPU (REQ)", c.GetTable().SortColCmd("%CPU/R", false), false), - ui.KeyShiftZ: ui.NewKeyAction("Sort %MEM (REQ)", c.GetTable().SortColCmd("%MEM/R", false), false), - tcell.KeyCtrlX: ui.NewKeyAction("Sort %CPU (LIM)", c.GetTable().SortColCmd("%CPU/L", false), false), - tcell.KeyCtrlQ: ui.NewKeyAction("Sort %MEM (LIM)", c.GetTable().SortColCmd("%MEM/L", false), false), + ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true), + ui.KeyShiftT: ui.NewKeyAction("Sort Restart", c.GetTable().SortColCmd("RESTARTS", false), false), }) + aa.Add(resourceSorters(c.GetTable())) } func (c *Container) k9sEnv() Env { diff --git a/internal/view/exec.go b/internal/view/exec.go index b229bb13..444c2075 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -135,8 +135,8 @@ const ( ) func ssh(a *App, node string) error { - nukeK9sShell(a.Conn()) - defer nukeK9sShell(a.Conn()) + nukeK9sShell(a) + defer nukeK9sShell(a) if err := launchShellPod(a, node); err != nil { return err } @@ -145,11 +145,15 @@ func ssh(a *App, node string) error { return nil } -func nukeK9sShell(c client.Connection) { +func nukeK9sShell(a *App) { + cl := a.Config.K9s.CurrentCluster + if !a.Config.K9s.Clusters[cl].FeatureGates.NodeShell { + return + } ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() - err := c.DialOrDie().CoreV1().Pods(k9sShellNS).Delete(ctx, k9sShellPodName(), metav1.DeleteOptions{}) + err := a.Conn().DialOrDie().CoreV1().Pods(k9sShellNS).Delete(ctx, k9sShellPodName(), metav1.DeleteOptions{}) if kerrors.IsNotFound(err) { return } diff --git a/internal/view/pod.go b/internal/view/pod.go index 90aa4cb4..00146ca0 100644 --- a/internal/view/pod.go +++ b/internal/view/pod.go @@ -52,18 +52,13 @@ func (p *Pod) bindKeys(aa ui.KeyActions) { } aa.Add(ui.KeyActions{ - ui.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(readyCol, true), false), - ui.KeyShiftT: ui.NewKeyAction("Sort Restart", p.GetTable().SortColCmd("RESTARTS", false), false), - ui.KeyShiftS: ui.NewKeyAction("Sort Status", p.GetTable().SortColCmd(statusCol, true), false), - ui.KeyShiftC: ui.NewKeyAction("Sort CPU", p.GetTable().SortColCmd(cpuCol, false), false), - ui.KeyShiftM: ui.NewKeyAction("Sort MEM", p.GetTable().SortColCmd(memCol, false), false), - ui.KeyShiftX: ui.NewKeyAction("Sort %CPU (REQ)", p.GetTable().SortColCmd("%CPU", false), false), - ui.KeyShiftZ: ui.NewKeyAction("Sort %MEM (REQ)", p.GetTable().SortColCmd("%MEM", false), false), - tcell.KeyCtrlX: ui.NewKeyAction("Sort %CPU (LIM)", p.GetTable().SortColCmd("%CPU/L", false), false), - tcell.KeyCtrlQ: ui.NewKeyAction("Sort %MEM (LIM)", p.GetTable().SortColCmd("%MEM/L", false), false), - ui.KeyShiftI: ui.NewKeyAction("Sort IP", p.GetTable().SortColCmd("IP", true), false), - ui.KeyShiftO: ui.NewKeyAction("Sort Node", p.GetTable().SortColCmd("NODE", true), false), + ui.KeyShiftR: ui.NewKeyAction("Sort Ready", p.GetTable().SortColCmd(readyCol, true), false), + ui.KeyShiftT: ui.NewKeyAction("Sort Restart", p.GetTable().SortColCmd("RESTARTS", false), false), + ui.KeyShiftS: ui.NewKeyAction("Sort Status", p.GetTable().SortColCmd(statusCol, true), false), + ui.KeyShiftI: ui.NewKeyAction("Sort IP", p.GetTable().SortColCmd("IP", true), false), + ui.KeyShiftO: ui.NewKeyAction("Sort Node", p.GetTable().SortColCmd("NODE", true), false), }) + aa.Add(resourceSorters(p.GetTable())) } func (p *Pod) selectedContainer() string { @@ -318,3 +313,14 @@ func podIsRunning(f dao.Factory, path string) bool { log.Debug().Msgf("Phase %#v", re.Phase(po)) return re.Phase(po) == render.Running } + +func resourceSorters(t *Table) ui.KeyActions { + return ui.KeyActions{ + ui.KeyShiftC: ui.NewKeyAction("Sort CPU", t.SortColCmd(cpuCol, false), false), + ui.KeyShiftM: ui.NewKeyAction("Sort MEM", t.SortColCmd(memCol, false), false), + ui.KeyShiftX: ui.NewKeyAction("Sort %CPU (REQ)", t.SortColCmd("%CPU/R", false), false), + ui.KeyShiftZ: ui.NewKeyAction("Sort %MEM (REQ)", t.SortColCmd("%MEM/R", false), false), + tcell.KeyCtrlX: ui.NewKeyAction("Sort %CPU (LIM)", t.SortColCmd("%CPU/L", false), false), + tcell.KeyCtrlQ: ui.NewKeyAction("Sort %MEM (LIM)", t.SortColCmd("%MEM/L", false), false), + } +} diff --git a/internal/view/popeye.go b/internal/view/popeye.go index e2999073..7992627e 100644 --- a/internal/view/popeye.go +++ b/internal/view/popeye.go @@ -53,8 +53,13 @@ func (p *Popeye) decorateRows(data render.TableData) render.TableData { } sum += n } - score := sum / len(data.RowEvents) - p.GetTable().Extras = fmt.Sprintf("Score %d -- %s", score, grade(score)) + score, letter := 0, render.NAValue + if len(data.RowEvents) > 0 { + score = sum / len(data.RowEvents) + letter = grade(score) + } + p.GetTable().Extras = fmt.Sprintf("Score %d -- %s", score, letter) + return data } diff --git a/internal/view/pvc.go b/internal/view/pvc.go new file mode 100644 index 00000000..6bb3c542 --- /dev/null +++ b/internal/view/pvc.go @@ -0,0 +1,32 @@ +package view + +import ( + "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/render" + "github.com/derailed/k9s/internal/ui" +) + +// PersistentVolumeClaim represents a PVC custom viewer. +type PersistentVolumeClaim struct { + ResourceViewer +} + +// NewPersistentVolumeClaim returns a new viewer. +func NewPersistentVolumeClaim(gvr client.GVR) ResourceViewer { + d := PersistentVolumeClaim{ + ResourceViewer: NewBrowser(gvr), + } + d.SetBindKeysFn(d.bindKeys) + d.GetTable().SetColorerFn(render.PersistentVolumeClaim{}.ColorerFunc()) + + return &d +} + +func (d *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), + }) +} diff --git a/internal/view/registrar.go b/internal/view/registrar.go index b2979686..cfeb4922 100644 --- a/internal/view/registrar.go +++ b/internal/view/registrar.go @@ -45,6 +45,9 @@ func coreViewers(vv MetaViewers) { vv[client.NewGVR("v1/secrets")] = MetaViewer{ viewerFn: NewSecret, } + vv[client.NewGVR("v1/persistentvolumeclaims")] = MetaViewer{ + viewerFn: NewPersistentVolumeClaim, + } } func miscViewers(vv MetaViewers) { diff --git a/internal/watch/factory.go b/internal/watch/factory.go index 23e33298..e2d6cc2d 100644 --- a/internal/watch/factory.go +++ b/internal/watch/factory.go @@ -163,13 +163,6 @@ func (f *Factory) isClusterWide() bool { // CanForResource return an informer is user has access. func (f *Factory) CanForResource(ns, gvr string, verbs []string) (informers.GenericInformer, error) { - // If user can access resource cluster wide, prefer cluster wide factory. - if !client.IsClusterWide(ns) { - auth, err := f.Client().CanI(client.AllNamespaces, gvr, verbs) - if auth && err == nil { - return f.ForResource(client.AllNamespaces, gvr), nil - } - } auth, err := f.Client().CanI(ns, gvr, verbs) if err != nil { return nil, err diff --git a/main.go b/main.go index a040a539..77751e55 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "os" + _ "net/http/pprof" + "github.com/derailed/k9s/cmd" "github.com/derailed/k9s/internal/config" "github.com/rs/zerolog" From 4bd37a492f108055e2670c9e3d1f2d0b17872e50 Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 14 May 2020 18:39:34 -0600 Subject: [PATCH 2/6] update shell pod config --- internal/config/cluster.go | 7 ++++++ internal/config/config_test.go | 26 ++++++++++++++++++-- internal/config/k9s.go | 19 ++++----------- internal/config/shell_pod.go | 44 ++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 internal/config/shell_pod.go diff --git a/internal/config/cluster.go b/internal/config/cluster.go index bfc3eb88..16ed54c9 100644 --- a/internal/config/cluster.go +++ b/internal/config/cluster.go @@ -9,6 +9,7 @@ type Cluster struct { Namespace *Namespace `yaml:"namespace"` View *View `yaml:"view"` FeatureGates *FeatureGates `yaml:"featureGates"` + ShellPod *ShellPod `yaml:"shellPod"` } // NewCluster creates a new cluster configuration. @@ -17,6 +18,7 @@ func NewCluster() *Cluster { Namespace: NewNamespace(), View: NewView(), FeatureGates: NewFeatureGates(), + ShellPod: NewShellPod(), } } @@ -34,4 +36,9 @@ func (c *Cluster) Validate(conn client.Connection, ks KubeSettings) { c.View = NewView() } c.View.Validate() + + if c.ShellPod == nil { + c.ShellPod = NewShellPod() + } + c.ShellPod.Validate(conn, ks) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 5de60462..97631b05 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -261,7 +261,6 @@ func TestSetup(t *testing.T) { var expectedConfig = `k9s: refreshRate: 100 - dockerShellImage: busybox:1.31 headless: false readOnly: true noIcons: false @@ -284,6 +283,12 @@ var expectedConfig = `k9s: active: po featureGates: nodeShell: false + shellPod: + image: busybox:1.31 + namespace: default + limits: + cpu: 100m + memory: 100Mi fred: namespace: active: default @@ -297,6 +302,12 @@ var expectedConfig = `k9s: active: po featureGates: nodeShell: false + shellPod: + image: busybox:1.31 + namespace: default + limits: + cpu: 100m + memory: 100Mi minikube: namespace: active: kube-system @@ -310,6 +321,12 @@ var expectedConfig = `k9s: active: ctx featureGates: nodeShell: false + shellPod: + image: busybox:1.31 + namespace: default + limits: + cpu: 100m + memory: 100Mi thresholds: cpu: critical: 90 @@ -321,7 +338,6 @@ var expectedConfig = `k9s: var resetConfig = `k9s: refreshRate: 2 - dockerShellImage: busybox:1.31 headless: false readOnly: false noIcons: false @@ -344,6 +360,12 @@ var resetConfig = `k9s: active: po featureGates: nodeShell: false + shellPod: + image: busybox:1.31 + namespace: default + limits: + cpu: 100m + memory: 100Mi thresholds: cpu: critical: 90 diff --git a/internal/config/k9s.go b/internal/config/k9s.go index 605f3010..a65dff42 100644 --- a/internal/config/k9s.go +++ b/internal/config/k9s.go @@ -2,16 +2,11 @@ package config import "github.com/derailed/k9s/internal/client" -const ( - defaultRefreshRate = 2 - // DefaultDockerShellImage specifies the docker image and tag for shelling into nodes. - DefaultDockerShellImage = "busybox:1.31" -) +const defaultRefreshRate = 2 // K9s tracks K9s configuration options. type K9s struct { RefreshRate int `yaml:"refreshRate"` - DockerShellImage string `yaml:"dockerShellImage"` Headless bool `yaml:"headless"` ReadOnly bool `yaml:"readOnly"` NoIcons bool `yaml:"noIcons"` @@ -29,11 +24,10 @@ type K9s struct { // NewK9s create a new K9s configuration. func NewK9s() *K9s { return &K9s{ - RefreshRate: defaultRefreshRate, - DockerShellImage: DefaultDockerShellImage, - Logger: NewLogger(), - Clusters: make(map[string]*Cluster), - Thresholds: NewThreshold(), + RefreshRate: defaultRefreshRate, + Logger: NewLogger(), + Clusters: make(map[string]*Cluster), + Thresholds: NewThreshold(), } } @@ -104,9 +98,6 @@ func (k *K9s) validateDefaults() { if k.RefreshRate <= 0 { k.RefreshRate = defaultRefreshRate } - if k.DockerShellImage == "" { - k.DockerShellImage = DefaultDockerShellImage - } } func (k *K9s) validateClusters(c client.Connection, ks KubeSettings) { diff --git a/internal/config/shell_pod.go b/internal/config/shell_pod.go new file mode 100644 index 00000000..52555c36 --- /dev/null +++ b/internal/config/shell_pod.go @@ -0,0 +1,44 @@ +package config + +import ( + "github.com/derailed/k9s/internal/client" + v1 "k8s.io/api/core/v1" +) + +const defaultDockerShellImage = "busybox:1.31" + +// Limits represents resource limits. +type Limits map[v1.ResourceName]string + +// ShellPod represents k9s shell configuration. +type ShellPod struct { + Image string `json:"Image"` + Namespace string `json:"namespace"` + Limits Limits `json:"resources,omitempty"` +} + +// NewShellPod returns a new instance. +func NewShellPod() *ShellPod { + return &ShellPod{ + Image: defaultDockerShellImage, + Namespace: "default", + Limits: defaultLimits(), + } +} + +// Validate validates the configuration. +func (s *ShellPod) Validate(client.Connection, KubeSettings) { + if s.Image == "" { + s.Image = defaultDockerShellImage + } + if len(s.Limits) == 0 { + s.Limits = defaultLimits() + } +} + +func defaultLimits() Limits { + return Limits{ + v1.ResourceCPU: "100m", + v1.ResourceMemory: "100Mi", + } +} From 175a16cfce8d6188aed2bb7b245075dd4bcf6753 Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 14 May 2020 18:41:40 -0600 Subject: [PATCH 3/6] update shell config --- internal/view/exec.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/view/exec.go b/internal/view/exec.go index 444c2075..62cff81e 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -12,7 +12,6 @@ import ( "time" "github.com/derailed/k9s/internal/client" - "github.com/derailed/k9s/internal/config" "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -163,11 +162,7 @@ func nukeK9sShell(a *App) { } func launchShellPod(a *App, node string) error { - img := a.Config.K9s.DockerShellImage - if img == "" { - img = config.DefaultDockerShellImage - } - spec := k9sShellPod(node, img) + spec := k9sShellPod(node, a.Config.K9s.ActiveCluster().ShellPod) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() dial := a.Conn().DialOrDie().CoreV1().Pods(k9sShellNS) From cb456883ecef2d4d7958ce521b0fb29e8312d61c Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 14 May 2020 18:43:33 -0600 Subject: [PATCH 4/6] checkpoint --- internal/view/exec.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/view/exec.go b/internal/view/exec.go index 62cff81e..f73f3b2d 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -12,10 +12,10 @@ import ( "time" "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/config" "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" @@ -193,14 +193,14 @@ func k9sShellPodName() string { return fmt.Sprintf("%s-%d", k9sShell, os.Getpid()) } -func k9sShellPod(node, image string) v1.Pod { +func k9sShellPod(node string, cfg *config.ShellPod) v1.Pod { var grace int64 var priv bool = true return v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: k9sShellPodName(), - Namespace: k9sShellNS, + Namespace: cfg.Namespace, }, Spec: v1.PodSpec{ NodeName: node, @@ -221,7 +221,7 @@ func k9sShellPod(node, image string) v1.Pod { Containers: []v1.Container{ { Name: k9sShell, - Image: image, + Image: cfg.Image, VolumeMounts: []v1.VolumeMount{ { Name: "root-vol", @@ -229,13 +229,8 @@ func k9sShellPod(node, image string) v1.Pod { ReadOnly: true, }, }, - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("200m"), - v1.ResourceMemory: resource.MustParse("100Mi"), - }, - }, - Stdin: true, + Resources: asResource(cfg.Limits), + Stdin: true, SecurityContext: &v1.SecurityContext{ Privileged: &priv, }, From e181fe859c07623b06625a698d2f3cd0b787fbd7 Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 14 May 2020 18:44:43 -0600 Subject: [PATCH 5/6] checkpoint --- internal/view/exec.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/view/exec.go b/internal/view/exec.go index f73f3b2d..e84b9a8e 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -16,6 +16,7 @@ import ( "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" @@ -239,3 +240,12 @@ func k9sShellPod(node string, cfg *config.ShellPod) v1.Pod { }, } } + +func asResource(r config.Limits) v1.ResourceRequirements { + return v1.ResourceRequirements{ + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse(r[v1.ResourceCPU]), + v1.ResourceMemory: resource.MustParse(r[v1.ResourceMemory]), + }, + } +} From e04d7da461d348c11d8132762d651e2973e72386 Mon Sep 17 00:00:00 2001 From: derailed Date: Fri, 15 May 2020 16:32:15 -0600 Subject: [PATCH 6/6] add release notes --- change_logs/release_v0.19.5.md | 72 ++++++++++++++++++++++++++++++++++ internal/view/exec.go | 13 +++--- 2 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 change_logs/release_v0.19.5.md diff --git a/change_logs/release_v0.19.5.md b/change_logs/release_v0.19.5.md new file mode 100644 index 00000000..6ab47564 --- /dev/null +++ b/change_logs/release_v0.19.5.md @@ -0,0 +1,72 @@ + + +# Release v0.19.5 + +## Notes + +Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better is as ever very much noticed and appreciated! + +Also if you dig this tool, consider joining our [sponsorhip program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) + +On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM) + +--- + +## A Word From Out Sponsors... + +First off, I would like to send a `Big Thank You` to the following generous K9s friends for joining our sponsorship program and supporting this project! + +* [Tommy Dejbjerg Pedersen](https://github.com/tpedersen123) +* [Matt Welke](https://github.com/mattwelke) + +## Disruption In The Force + +During this drop, I've gotten totally slammed by other forces ;( I've had so many disruptions that affected my `quasi` normal flow hence this drop might be a bit wonky ;( So please proceed with caution!! + +As always please help me flush/report issues and I'll address them promptly! Thank you so much for your understanding and patience!! 🙏👨‍❤️‍👨😍 + +## Improved Node Shell Usability + +In this drop we've changed the configuration of the node shell action that let's you shell into nodes. Big thanks to [Patrick Decat](https://github.com/pdecat) for helping us flesh out this beta feature! We've added configuration to not only customize the image but also the resources and namespace on how to run these K9s pods on your clusters. The new configuration is set at the cluster scope level. + +Here is an example of the new pod shell config options: + +```yaml +# $HOME/.k9s/config.yml +k9s: + clusters: + blee: + featureGates: + # You must enable the nodeShell feature gate to enable shelling into nodes + nodeShell: true + # NEW! You can now tune the pod specification: currently image, namespace and resources + shellPod: + image: cool_kid_admin:42 + namespace: blee + limits: + cpu: 100m + memory: 100Mi +``` + +## Resolved Bugs/Features/PRs + +* [Issue #714](https://github.com/derailed/k9s/issues/714) +* [Issue #713](https://github.com/derailed/k9s/issues/713) +* [Issue #708](https://github.com/derailed/k9s/issues/708) +* [Issue #707](https://github.com/derailed/k9s/issues/707) +* [Issue #705](https://github.com/derailed/k9s/issues/705) +* [Issue #704](https://github.com/derailed/k9s/issues/704) +* [Issue #702](https://github.com/derailed/k9s/issues/702) +* [Issue #700](https://github.com/derailed/k9s/issues/700) Fingers and toes crossed ;) +* [Issue #694](https://github.com/derailed/k9s/issues/694) +* [Issue #663](https://github.com/derailed/k9s/issues/663) Partially - should be better launching in a given namespace ie k9s -n fred?? +* [Issue #702](https://github.com/derailed/k9s/issues/702) +* [PR #709](https://github.com/derailed/k9s/pull/709) All credits goes to [Namco](https://github.com/namco1992)!! +* [PR #706](https://github.com/derailed/k9s/pull/706) Big Thanks to [M. Tarık Yurt](https://github.com/mtyurt)! +* [PR #704](https://github.com/derailed/k9s/pull/704) Atta Boy!! [psvo](https://github.com/psvo) +* [PR #696](https://github.com/derailed/k9s/pull/696) Thank you! Credits to [Christian Köhn](https://github.com/ckoehn) +* [PR #691](https://github.com/derailed/k9s/pull/691) Mega Thanks To [Pavel Tumik](https://github.com/sagor999)! + +--- + + © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) diff --git a/internal/view/exec.go b/internal/view/exec.go index e84b9a8e..aadaaa40 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -129,7 +129,6 @@ func clearScreen() { const ( k9sShell = "k9s-shell" - k9sShellNS = "default" k9sShellRetryCount = 10 k9sShellRetryDelay = 500 * time.Millisecond ) @@ -140,7 +139,8 @@ func ssh(a *App, node string) error { if err := launchShellPod(a, node); err != nil { return err } - shellIn(a, client.FQN(k9sShellNS, k9sShellPodName()), k9sShell) + ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace + shellIn(a, client.FQN(ns, k9sShellPodName()), k9sShell) return nil } @@ -150,10 +150,12 @@ func nukeK9sShell(a *App) { if !a.Config.K9s.Clusters[cl].FeatureGates.NodeShell { return } + + ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() - err := a.Conn().DialOrDie().CoreV1().Pods(k9sShellNS).Delete(ctx, k9sShellPodName(), metav1.DeleteOptions{}) + err := a.Conn().DialOrDie().CoreV1().Pods(ns).Delete(ctx, k9sShellPodName(), metav1.DeleteOptions{}) if kerrors.IsNotFound(err) { return } @@ -163,16 +165,17 @@ func nukeK9sShell(a *App) { } func launchShellPod(a *App, node string) error { + ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace spec := k9sShellPod(node, a.Config.K9s.ActiveCluster().ShellPod) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() - dial := a.Conn().DialOrDie().CoreV1().Pods(k9sShellNS) + dial := a.Conn().DialOrDie().CoreV1().Pods(ns) if _, err := dial.Create(ctx, &spec, metav1.CreateOptions{}); err != nil { return err } for i := 0; i < k9sShellRetryCount; i++ { - o, err := a.factory.Get("v1/pods", client.FQN(k9sShellNS, k9sShellPodName()), true, labels.Everything()) + o, err := a.factory.Get("v1/pods", client.FQN(ns, k9sShellPodName()), true, labels.Everything()) if err != nil { time.Sleep(k9sShellRetryDelay) continue