From 5893cffb0d7c65f67b5bcc1e6d768bc3f9ea9f7b Mon Sep 17 00:00:00 2001 From: derailed Date: Thu, 2 Jul 2020 17:58:25 -0600 Subject: [PATCH] add dir view --- .gitignore | 1 + change_logs/release_v0.20.6.md | 18 ++- go.mod | 6 +- go.sum | 5 + internal/config/alias.go | 1 + internal/dao/dir.go | 62 ++++++++++ internal/dao/registry.go | 9 +- internal/model/registry.go | 4 + internal/render/dir.go | 64 +++++++++++ internal/view/alias.go | 7 +- internal/view/app.go | 18 +++ internal/view/command.go | 5 + internal/view/dir.go | 203 +++++++++++++++++++++++++++++++++ internal/view/exec.go | 44 +++++++ internal/view/help.go | 2 +- internal/view/meow.go | 2 +- internal/view/registrar.go | 1 - internal/watch/factory.go | 4 +- 18 files changed, 443 insertions(+), 13 deletions(-) create mode 100644 internal/dao/dir.go create mode 100644 internal/render/dir.go create mode 100644 internal/view/dir.go diff --git a/.gitignore b/.gitignore index 301be356..e3270627 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ pod1.go faas .settings/* demos +/code \ No newline at end of file diff --git a/change_logs/release_v0.20.6.md b/change_logs/release_v0.20.6.md index 017e2aa7..d9c94406 100644 --- a/change_logs/release_v0.20.6.md +++ b/change_logs/release_v0.20.6.md @@ -6,7 +6,7 @@ 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) +If you feel K9s is helping your Kubernetes journey, please 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) @@ -19,7 +19,18 @@ First off, I would like to send a `Big Thank You` to the following generous K9s * [Remo Eichenberger](https://github.com/remoe) * [Ken Ahrens](https://github.com/kenahrens) -Maintenance Release! +## Moving Forward! + +In this drop, we've added a port-forward indicator to visually see if a port-forward is active on a pod/container. You can also navigate directly to the port-forward view using the new shortcut `f` available in +pod and container view. + +## Manifest That! + +Ever wanted to manipulate your Kubernetes manifests directly in K9s? `Yes Please!!` + +We are introducing a new view namely `directory` aka `dir`. Using this command you can list/traverse a given directory structure containing your Kubernetes manifests using a new `:dir /fred` command. +From there you can view/edit your manifests and also deploy or delete these resources for your cluster directly from K9s. Just like `kubectl` you can apply/delete an entire directory or a single manifest. +How cool is that? ## Resolved Bugs/Features/PRs @@ -27,7 +38,8 @@ Maintenance Release! * [Issue #774](https://github.com/derailed/k9s/issues/774) * [Issue #761](https://github.com/derailed/k9s/issues/761) * [Issue #759](https://github.com/derailed/k9s/issues/759) -* [Issue #756](https://github.com/derailed/k9s/issues/756) +* [Issue #758](https://github.com/derailed/k9s/issues/758) +* [PR #746](https://github.com/derailed/k9s/pull/746) Big Thanks to [Groselt](https://github.com/groselt)! --- diff --git a/go.mod b/go.mod index 428a3eb9..a5383e4b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( 9fans.net/go v0.0.2 github.com/atotto/clipboard v0.1.2 + github.com/coreos/etcd v3.3.10+incompatible github.com/derailed/popeye v0.8.6 github.com/derailed/tview v0.3.10 github.com/drone/envsubst v1.0.2 // indirect @@ -13,7 +14,9 @@ require ( github.com/gdamore/tcell v1.3.0 github.com/ghodss/yaml v1.0.0 github.com/golang/protobuf v1.4.2 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect + github.com/kylelemons/godebug v1.1.0 + github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 + github.com/mattn/go-isatty v0.0.11 github.com/mattn/go-runewidth v0.0.9 github.com/openfaas/faas v0.0.0-20200207215241-6afae214e3ec github.com/openfaas/faas-cli v0.0.0-20200124160744-30b7cec9634c @@ -30,6 +33,7 @@ require ( golang.org/x/text v0.3.2 google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587 // indirect google.golang.org/grpc v1.29.1 // indirect + gopkg.in/fsnotify.v1 v1.4.7 gopkg.in/yaml.v2 v2.2.8 helm.sh/helm/v3 v3.2.0 k8s.io/api v0.18.2 diff --git a/go.sum b/go.sum index 9d545224..9f47d5d1 100644 --- a/go.sum +++ b/go.sum @@ -114,15 +114,18 @@ github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -415,6 +418,8 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= diff --git a/internal/config/alias.go b/internal/config/alias.go index 04797e3e..5fc25bbc 100644 --- a/internal/config/alias.go +++ b/internal/config/alias.go @@ -144,6 +144,7 @@ func (a *Aliases) loadDefaultAliases() { a.declare("aliases", "alias", "a") a.declare("popeye", "pop") a.declare("helm", "charts", "chart", "hm") + a.declare("dir", "d") a.declare("contexts", "context", "ctx") a.declare("users", "user", "usr") a.declare("groups", "group", "grp") diff --git a/internal/dao/dir.go b/internal/dao/dir.go new file mode 100644 index 00000000..c43880df --- /dev/null +++ b/internal/dao/dir.go @@ -0,0 +1,62 @@ +package dao + +import ( + "context" + "errors" + "io/ioutil" + "path/filepath" + "regexp" + "strings" + + "github.com/derailed/k9s/internal" + "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/render" + "k8s.io/apimachinery/pkg/runtime" +) + +var _ Accessor = (*Dir)(nil) + +// Dir tracks standard and custom command aliases. +type Dir struct { + NonResource +} + +// NewDir returns a new set of aliases. +func NewDir(f Factory) *Dir { + var a Dir + a.Init(f, client.NewGVR("dir")) + return &a +} + +var yamlRX = regexp.MustCompile(`.*\.(yml|yaml)`) + +// List returns a collection of aliases. +func (a *Dir) List(ctx context.Context, _ string) ([]runtime.Object, error) { + dir, ok := ctx.Value(internal.KeyPath).(string) + if !ok { + return nil, errors.New("No dir in context") + } + + files, err := ioutil.ReadDir(dir) + if err != nil { + return nil, err + } + + oo := make([]runtime.Object, 0, len(files)) + for _, f := range files { + if strings.HasPrefix(f.Name(), ".") || !f.IsDir() && !yamlRX.MatchString(f.Name()) { + continue + } + oo = append(oo, render.DirRes{ + Path: filepath.Join(dir, f.Name()), + Info: f, + }) + } + + return oo, err +} + +// Get fetch a resource. +func (a *Dir) Get(_ context.Context, _ string) (runtime.Object, error) { + return nil, errors.New("NYI!!") +} diff --git a/internal/dao/registry.go b/internal/dao/registry.go index b4e446db..6ebaf4e0 100644 --- a/internal/dao/registry.go +++ b/internal/dao/registry.go @@ -51,6 +51,7 @@ func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) { client.NewGVR("popeye"): &Popeye{}, client.NewGVR("sanitizer"): &Popeye{}, client.NewGVR("helm"): &Helm{}, + client.NewGVR("dir"): &Dir{}, } r, ok := m[gvr] @@ -152,6 +153,12 @@ func loadK9s(m ResourceMetas) { ShortNames: []string{"hz", "pu"}, Categories: []string{"k9s"}, } + m[client.NewGVR("dir")] = metav1.APIResource{ + Name: "dir", + Kind: "Dir", + SingularName: "dir", + Categories: []string{"k9s"}, + } m[client.NewGVR("xrays")] = metav1.APIResource{ Name: "xray", Kind: "XRays", @@ -176,7 +183,7 @@ func loadK9s(m ResourceMetas) { Name: "popeye", Kind: "Popeye", SingularName: "popeye", - Namespaced: true, + Namespaced: true, Verbs: []string{}, Categories: []string{"k9s"}, } diff --git a/internal/model/registry.go b/internal/model/registry.go index 40c47a12..39919021 100644 --- a/internal/model/registry.go +++ b/internal/model/registry.go @@ -14,6 +14,10 @@ var Registry = map[string]ResourceMeta{ DAO: &dao.Reference{}, Renderer: &render.Reference{}, }, + "dir": { + DAO: &dao.Dir{}, + Renderer: &render.Dir{}, + }, "helm": { DAO: &dao.Helm{}, Renderer: &render.Helm{}, diff --git a/internal/render/dir.go b/internal/render/dir.go new file mode 100644 index 00000000..e3aac63f --- /dev/null +++ b/internal/render/dir.go @@ -0,0 +1,64 @@ +package render + +import ( + "fmt" + "os" + + "github.com/gdamore/tcell" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Dir renders a directory entry to screen. +type Dir struct{} + +// ColorerFunc colors a resource row. +func (Dir) ColorerFunc() ColorerFunc { + return func(ns string, _ Header, re RowEvent) tcell.Color { + return tcell.ColorCadetBlue + } +} + +// Header returns a header row. +func (Dir) Header(ns string) Header { + return Header{ + HeaderColumn{Name: "NAME"}, + } +} + +// Render renders a K8s resource to screen. +// BOZO!! Pass in a row with pre-alloc fields?? +func (Dir) Render(o interface{}, ns string, r *Row) error { + d, ok := o.(DirRes) + if !ok { + return fmt.Errorf("expected DirRes, but got %T", o) + } + + name := "🦄 " + if d.Info.IsDir() { + name = "📁 " + } + name += d.Info.Name() + r.ID, r.Fields = d.Path, append(r.Fields, name) + + return nil +} + +// ---------------------------------------------------------------------------- +// Helpers... + +// DirRes represents an alias resource. +type DirRes struct { + Info os.FileInfo + Path string +} + +// GetObjectKind returns a schema object. +func (DirRes) GetObjectKind() schema.ObjectKind { + return nil +} + +// DeepCopyObject returns a container copy. +func (d DirRes) DeepCopyObject() runtime.Object { + return d +} diff --git a/internal/view/alias.go b/internal/view/alias.go index 6b5b08a3..da966c0e 100644 --- a/internal/view/alias.go +++ b/internal/view/alias.go @@ -58,6 +58,10 @@ func (a *Alias) bindKeys(aa ui.KeyActions) { } func (a *Alias) gotoCmd(evt *tcell.EventKey) *tcell.EventKey { + if a.GetTable().CmdBuff().IsActive() { + return a.GetTable().activateCmd(evt) + } + r, _ := a.GetTable().GetSelection() if r != 0 { s := ui.TrimCell(a.GetTable().SelectTable, r, 1) @@ -68,8 +72,5 @@ func (a *Alias) gotoCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } - if a.GetTable().CmdBuff().IsActive() { - return a.GetTable().activateCmd(evt) - } return evt } diff --git a/internal/view/app.go b/internal/view/app.go index 65cfbf2d..27c6cad0 100644 --- a/internal/view/app.go +++ b/internal/view/app.go @@ -523,6 +523,24 @@ func (a *App) meowCmd(msg string) { } } +func (a *App) dirCmd(path string) error { + log.Debug().Msgf("DIR PATH %q", path) + _, err := os.Stat(path) + if err != nil { + return err + } + if path == "." { + dir, err := os.Getwd() + if err == nil { + path = dir + } + } + a.Content.Stack.Clear() + a.cmdHistory.Push("dir " + path) + + return a.inject(NewDir(path)) +} + func (a *App) helpCmd(evt *tcell.EventKey) *tcell.EventKey { if a.CmdBuff().InCmdMode() { return evt diff --git a/internal/view/command.go b/internal/view/command.go index aee0d39a..a0236725 100644 --- a/internal/view/command.go +++ b/internal/view/command.go @@ -124,6 +124,11 @@ func (c *Command) run(cmd, path string, clearStack bool) error { return useContext(c.app, cmds[1]) } return c.exec(cmd, gvr, c.componentFor(gvr, path, v), clearStack) + case "dir": + if len(cmds) != 2 { + return errors.New("You must specify a directory") + } + return c.app.dirCmd(cmds[1]) default: // checks if Command includes a namespace ns := c.app.Config.ActiveNamespace() diff --git a/internal/view/dir.go b/internal/view/dir.go new file mode 100644 index 00000000..15830f4a --- /dev/null +++ b/internal/view/dir.go @@ -0,0 +1,203 @@ +package view + +import ( + "context" + "errors" + "fmt" + "io/ioutil" + "path" + + "github.com/derailed/k9s/internal" + "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/render" + "github.com/derailed/k9s/internal/ui" + "github.com/derailed/k9s/internal/ui/dialog" + "github.com/gdamore/tcell" + "github.com/rs/zerolog/log" +) + +// Dir represents a command directory view. +type Dir struct { + ResourceViewer + path string +} + +func NewDir(path string) ResourceViewer { + d := Dir{ + ResourceViewer: NewBrowser(client.NewGVR("dir")), + path: path, + } + d.GetTable().SetBorderFocusColor(tcell.ColorAliceBlue) + d.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorAliceBlue, tcell.AttrNone) + d.SetBindKeysFn(d.bindKeys) + d.SetContextFn(d.dirContext) + d.GetTable().SetColorerFn(render.Dir{}.ColorerFunc()) + + return &d +} + +// Init initializes the view. +func (d *Dir) Init(ctx context.Context) error { + if err := d.ResourceViewer.Init(ctx); err != nil { + return err + } + + return nil +} + +func (d *Dir) dirContext(ctx context.Context) context.Context { + return context.WithValue(ctx, internal.KeyPath, d.path) +} + +func (d *Dir) bindKeys(aa ui.KeyActions) { + aa.Delete(ui.KeyShiftA, tcell.KeyCtrlS, tcell.KeyCtrlSpace, ui.KeySpace) + aa.Delete(tcell.KeyCtrlW, tcell.KeyCtrlL, tcell.KeyCtrlD, tcell.KeyCtrlZ) + aa.Add(ui.KeyActions{ + ui.KeyA: ui.NewKeyAction("Apply", d.applyCmd, true), + ui.KeyD: ui.NewKeyAction("Delete", d.delCmd, true), + ui.KeyE: ui.NewKeyAction("Edit", d.editCmd, true), + ui.KeyY: ui.NewKeyAction("YAML", d.viewCmd, true), + tcell.KeyEnter: ui.NewKeyAction("Goto", d.gotoCmd, true), + }) +} + +func (d *Dir) viewCmd(evt *tcell.EventKey) *tcell.EventKey { + sel := d.GetTable().GetSelectedItem() + if sel == "" { + return evt + } + + if path.Ext(sel) == "" { + return nil + } + + yaml, err := ioutil.ReadFile(sel) + if err != nil { + d.App().Flash().Err(err) + return nil + } + + details := NewDetails(d.App(), "YAML", sel, true).Update(string(yaml)) + if err := d.App().inject(details); err != nil { + d.App().Flash().Err(err) + } + + return nil +} + +func isManifest(s string) bool { + ext := path.Ext(s) + return ext == ".yml" || ext == ".yaml" +} + +func (d *Dir) editCmd(evt *tcell.EventKey) *tcell.EventKey { + sel := d.GetTable().GetSelectedItem() + if sel == "" { + return evt + } + + log.Debug().Msgf("Selected %q", sel) + if !isManifest(sel) { + d.App().Flash().Errf("you must select a manifest") + return nil + } + + d.Stop() + defer d.Start() + if !edit(d.App(), shellOpts{clear: true, args: []string{sel}}) { + d.App().Flash().Err(errors.New("Failed to launch editor")) + } + + return nil +} + +func (d *Dir) gotoCmd(evt *tcell.EventKey) *tcell.EventKey { + if d.GetTable().CmdBuff().IsActive() { + return d.GetTable().activateCmd(evt) + } + + sel := d.GetTable().GetSelectedItem() + if sel == "" { + return evt + } + + if isManifest(sel) { + d.App().Flash().Errf("you must select a directory") + return nil + } + + v := NewDir(sel) + if err := d.App().inject(v); err != nil { + d.App().Flash().Err(err) + } + + return evt +} + +func (d *Dir) applyCmd(evt *tcell.EventKey) *tcell.EventKey { + sel := d.GetTable().GetSelectedItem() + if sel == "" { + return evt + } + + if !isManifest(sel) { + d.App().Flash().Errf("you must select a manifest") + return nil + } + + d.Stop() + defer d.Start() + { + args := make([]string, 0, 10) + args = append(args, "apply") + args = append(args, "-f") + args = append(args, sel) + res, err := runKu(d.App(), shellOpts{clear: false, args: args}) + if err != nil { + res = "error:\n " + err.Error() + } else { + res = "message:\n " + res + } + + details := NewDetails(d.App(), "Applied Manifest", sel, true).Update(res) + if err := d.App().inject(details); err != nil { + d.App().Flash().Err(err) + } + } + + return nil +} + +func (d *Dir) delCmd(evt *tcell.EventKey) *tcell.EventKey { + sel := d.GetTable().GetSelectedItem() + if sel == "" { + return evt + } + + if !isManifest(sel) { + d.App().Flash().Errf("you must select a manifest") + return nil + } + + d.Stop() + defer d.Start() + msg := fmt.Sprintf("Delete resource(s) in manifest %s", sel) + dialog.ShowConfirm(d.App().Content.Pages, "Confirm Delete", msg, func() { + args := make([]string, 0, 10) + args = append(args, "delete") + args = append(args, "-f") + args = append(args, sel) + res, err := runKu(d.App(), shellOpts{clear: false, args: args}) + if err != nil { + res = "error:\n " + err.Error() + "\nmessage:\n " + res + } else { + res = "message:\n " + res + } + details := NewDetails(d.App(), "Deleted Manifest", sel, true).Update(res) + if err := d.App().inject(details); err != nil { + d.App().Flash().Err(err) + } + }, func() {}) + + return nil +} diff --git a/internal/view/exec.go b/internal/view/exec.go index 4ef8bdba..46febaed 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -1,6 +1,7 @@ package view import ( + "bytes" "context" "errors" "fmt" @@ -123,6 +124,49 @@ func execute(opts shellOpts) error { } } +func runKu(a *App, opts shellOpts) (string, error) { + bin, err := exec.LookPath("kubectl") + if err != nil { + log.Error().Err(err).Msgf("kubectl command is not in your path") + return "", err + } + var args []string + if u, err := a.Conn().Config().ImpersonateUser(); err == nil { + args = append(args, "--as", u) + } + if g, err := a.Conn().Config().ImpersonateGroups(); err == nil { + args = append(args, "--as-group", g) + } + args = append(args, "--context", a.Config.K9s.CurrentContext) + if cfg := a.Conn().Config().Flags().KubeConfig; cfg != nil && *cfg != "" { + args = append(args, "--kubeconfig", *cfg) + } + if len(args) > 0 { + opts.args = append(args, opts.args...) + } + opts.binary, opts.background = bin, false + + return oneShoot(opts) +} + +func oneShoot(opts shellOpts) (string, error) { + if opts.clear { + clearScreen() + } + + log.Debug().Msgf("Running command> %s %s", opts.binary, strings.Join(opts.args, " ")) + cmd := exec.Command(opts.binary, opts.args...) + + var err error + buff := bytes.NewBufferString("") + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, buff, buff + _, _ = cmd.Stdout.Write([]byte(opts.banner)) + err = cmd.Run() + log.Debug().Msgf("RES %q", buff) + + return strings.Trim(buff.String(), "\n"), err +} + func clearScreen() { fmt.Print("\033[H\033[2J") } diff --git a/internal/view/help.go b/internal/view/help.go index 57e1a7a3..bfc39499 100644 --- a/internal/view/help.go +++ b/internal/view/help.go @@ -239,7 +239,7 @@ func (h *Help) showGeneral() model.MenuHints { Description: "Toggle Header", }, { - Mnemonic: "q", + Mnemonic: ":q", Description: "Quit", }, { diff --git a/internal/view/meow.go b/internal/view/meow.go index f4e7c031..27b11dcb 100644 --- a/internal/view/meow.go +++ b/internal/view/meow.go @@ -124,7 +124,7 @@ func (m *Meow) ExtraHints() map[string]string { } func (m *Meow) updateTitle() { - m.SetTitle(" Meow! ") + m.SetTitle(" Error ") } var cow = []string{ diff --git a/internal/view/registrar.go b/internal/view/registrar.go index fc7b5e2f..539ce953 100644 --- a/internal/view/registrar.go +++ b/internal/view/registrar.go @@ -90,7 +90,6 @@ func miscViewers(vv MetaViewers) { vv[client.NewGVR("sanitizer")] = MetaViewer{ viewerFn: NewSanitizer, } - } func appsViewers(vv MetaViewers) { diff --git a/internal/watch/factory.go b/internal/watch/factory.go index 53eb8510..6ec969e1 100644 --- a/internal/watch/factory.go +++ b/internal/watch/factory.go @@ -103,8 +103,8 @@ func (f *Factory) HasSynced(gvr, ns string) (bool, error) { } // Get retrieves a given resource. -func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) { - ns, n := namespaced(path) +func (f *Factory) Get(gvr, fqn string, wait bool, sel labels.Selector) (runtime.Object, error) { + ns, n := namespaced(fqn) inf, err := f.CanForResource(ns, gvr, []string{client.GetVerb}) if err != nil { return nil, err