cleaning up
parent
4f055089e3
commit
ca600cf322
|
|
@ -1,13 +1,11 @@
|
||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/atotto/clipboard"
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
|
@ -18,9 +16,6 @@ import (
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/cli-runtime/pkg/printers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Browser represents a generic resource browser.
|
// Browser represents a generic resource browser.
|
||||||
|
|
@ -28,20 +23,16 @@ type Browser struct {
|
||||||
*Table
|
*Table
|
||||||
|
|
||||||
namespaces map[int]string
|
namespaces map[int]string
|
||||||
gvr client.GVR
|
|
||||||
meta metav1.APIResource
|
meta metav1.APIResource
|
||||||
accessor dao.Accessor
|
accessor dao.Accessor
|
||||||
envFn EnvFunc
|
|
||||||
contextFn ContextFunc
|
contextFn ContextFunc
|
||||||
bindKeysFn BindKeysFunc
|
|
||||||
cancelFn context.CancelFunc
|
cancelFn context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBrowser returns a new browser.
|
// NewBrowser returns a new browser.
|
||||||
func NewBrowser(gvr client.GVR) ResourceViewer {
|
func NewBrowser(gvr client.GVR) ResourceViewer {
|
||||||
return &Browser{
|
return &Browser{
|
||||||
Table: NewTable(string(gvr)),
|
Table: NewTable(gvr),
|
||||||
gvr: gvr,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +43,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
b.BaseTitle = b.meta.Kind
|
||||||
|
|
||||||
if err = b.Table.Init(ctx); err != nil {
|
if err = b.Table.Init(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -66,13 +58,11 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
if b.bindKeysFn != nil {
|
if b.bindKeysFn != nil {
|
||||||
b.bindKeysFn(b.Actions())
|
b.bindKeysFn(b.Actions())
|
||||||
}
|
}
|
||||||
b.BaseTitle = b.meta.Kind
|
|
||||||
b.accessor, err = dao.AccessorFor(b.app.factory, b.gvr)
|
b.accessor, err = dao.AccessorFor(b.app.factory, b.gvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.envFn = b.defaultK9sEnv
|
|
||||||
b.setNamespace(b.App().Config.ActiveNamespace())
|
b.setNamespace(b.App().Config.ActiveNamespace())
|
||||||
row, _ := b.GetSelection()
|
row, _ := b.GetSelection()
|
||||||
if row == 0 && b.GetRowCount() > 0 {
|
if row == 0 && b.GetRowCount() > 0 {
|
||||||
|
|
@ -107,6 +97,58 @@ func (b *Browser) Start() {
|
||||||
b.GetModel().Watch(ctx)
|
b.GetModel().Watch(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop terminates browser updates.
|
||||||
|
func (b *Browser) Stop() {
|
||||||
|
if b.cancelFn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.Table.Stop()
|
||||||
|
log.Debug().Msgf("BROWSER <STOP> %q", b.gvr)
|
||||||
|
b.cancelFn()
|
||||||
|
b.cancelFn = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Browser) refresh() {
|
||||||
|
b.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the component name.
|
||||||
|
func (b *Browser) Name() string { return b.meta.Kind }
|
||||||
|
|
||||||
|
// SetContextFn populates a custom context.
|
||||||
|
func (b *Browser) SetContextFn(f ContextFunc) { b.contextFn = f }
|
||||||
|
|
||||||
|
// GetTable returns the underlying table.
|
||||||
|
func (b *Browser) GetTable() *Table { return b.Table }
|
||||||
|
|
||||||
|
// Aliases returns all available aliases.
|
||||||
|
func (b *Browser) Aliases() []string {
|
||||||
|
return append(b.meta.ShortNames, b.meta.SingularName, b.meta.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Model Protocol...
|
||||||
|
|
||||||
|
// TableLoadFailed notifies view something went south.
|
||||||
|
func (b *Browser) TableLoadFailed(err error) {
|
||||||
|
b.app.QueueUpdateDraw(func() {
|
||||||
|
b.app.Flash().Err(err)
|
||||||
|
b.App().ClearStatus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableDataChanged notifies view new data is available.
|
||||||
|
func (b *Browser) TableDataChanged(data render.TableData) {
|
||||||
|
b.app.QueueUpdateDraw(func() {
|
||||||
|
b.refreshActions()
|
||||||
|
b.Update(data)
|
||||||
|
b.App().ClearStatus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Actions...
|
||||||
|
|
||||||
func (b *Browser) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if !b.SearchBuff().InCmdMode() {
|
if !b.SearchBuff().InCmdMode() {
|
||||||
b.SearchBuff().Reset()
|
b.SearchBuff().Reset()
|
||||||
|
|
@ -143,82 +185,13 @@ func (b *Browser) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates browser updates.
|
|
||||||
func (b *Browser) Stop() {
|
|
||||||
if b.cancelFn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.Table.Stop()
|
|
||||||
log.Debug().Msgf("BROWSER <STOP> %q", b.gvr)
|
|
||||||
b.cancelFn()
|
|
||||||
b.cancelFn = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) refresh() {
|
|
||||||
b.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the component name.
|
|
||||||
func (b *Browser) Name() string { return b.meta.Kind }
|
|
||||||
|
|
||||||
// SetContextFn populates a custom context.
|
|
||||||
func (b *Browser) SetContextFn(f ContextFunc) { b.contextFn = f }
|
|
||||||
|
|
||||||
// SetBindKeysFn adds additional key bindings.
|
|
||||||
func (b *Browser) SetBindKeysFn(f BindKeysFunc) { b.bindKeysFn = f }
|
|
||||||
|
|
||||||
// SetEnvFn sets a function to pull viewer env vars for plugins.
|
|
||||||
func (b *Browser) SetEnvFn(f EnvFunc) { b.envFn = f }
|
|
||||||
|
|
||||||
// GVR returns a resource descriptor.
|
|
||||||
func (b *Browser) GVR() string { return string(b.gvr) }
|
|
||||||
|
|
||||||
// GetTable returns the underlying table.
|
|
||||||
func (b *Browser) GetTable() *Table { return b.Table }
|
|
||||||
|
|
||||||
// TableLoadFailed notifies view something went south.
|
|
||||||
func (b *Browser) TableLoadFailed(err error) {
|
|
||||||
b.app.QueueUpdateDraw(func() {
|
|
||||||
b.app.Flash().Err(err)
|
|
||||||
b.App().ClearStatus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableDataChanged notifies view new data is available.
|
|
||||||
func (b *Browser) TableDataChanged(data render.TableData) {
|
|
||||||
b.app.QueueUpdateDraw(func() {
|
|
||||||
b.refreshActions()
|
|
||||||
b.Update(data)
|
|
||||||
b.App().ClearStatus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Actions()...
|
|
||||||
|
|
||||||
func (b *Browser) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
||||||
path := b.GetSelectedItem()
|
|
||||||
if path == "" {
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
|
|
||||||
_, n := client.Namespaced(path)
|
|
||||||
log.Debug().Msgf("Copied selection to clipboard %q", n)
|
|
||||||
b.app.Flash().Info("Current selection copied to clipboard...")
|
|
||||||
if err := clipboard.WriteAll(n); err != nil {
|
|
||||||
b.app.Flash().Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := b.GetSelectedItem()
|
path := b.GetSelectedItem()
|
||||||
if b.filterCmd(evt) == nil || path == "" {
|
if b.filterCmd(evt) == nil || path == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
f := b.describeResource
|
f := describeResource
|
||||||
if b.enterFn != nil {
|
if b.enterFn != nil {
|
||||||
f = b.enterFn
|
f = b.enterFn
|
||||||
}
|
}
|
||||||
|
|
@ -257,111 +230,16 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) simpleDelete(selections []string, msg string) {
|
|
||||||
dialog.ShowConfirm(b.app.Content.Pages, "Confirm Delete", msg, func() {
|
|
||||||
b.ShowDeleted()
|
|
||||||
if len(selections) > 1 {
|
|
||||||
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
|
|
||||||
} else {
|
|
||||||
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
|
|
||||||
}
|
|
||||||
for _, sel := range selections {
|
|
||||||
if err := b.accessor.(dao.Nuker).Delete(sel, true, true); err != nil {
|
|
||||||
b.app.Flash().Errf("Delete failed with `%s", err)
|
|
||||||
} else {
|
|
||||||
b.GetTable().DeleteMark(sel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.refresh()
|
|
||||||
b.SelectRow(1, true)
|
|
||||||
}, func() {})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) resourceDelete(selections []string, msg string) {
|
|
||||||
dialog.ShowDelete(b.app.Content.Pages, msg, func(cascade, force bool) {
|
|
||||||
b.ShowDeleted()
|
|
||||||
if len(selections) > 1 {
|
|
||||||
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
|
|
||||||
} else {
|
|
||||||
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
|
|
||||||
}
|
|
||||||
for _, sel := range selections {
|
|
||||||
if err := b.accessor.(dao.Nuker).Delete(sel, cascade, force); err != nil {
|
|
||||||
b.app.Flash().Errf("Delete failed with `%s", err)
|
|
||||||
} else {
|
|
||||||
b.app.factory.DeleteForwarder(sel)
|
|
||||||
b.GetTable().DeleteMark(sel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.refresh()
|
|
||||||
b.SelectRow(1, true)
|
|
||||||
}, func() {})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := b.GetSelectedItem()
|
path := b.GetSelectedItem()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
b.describeResource(b.app, b.GetModel().GetNamespace(), string(b.gvr), path)
|
describeResource(b.app, b.GetModel().GetNamespace(), string(b.gvr), path)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) describeResource(app *App, _, _, sel string) {
|
|
||||||
ns, n := client.Namespaced(sel)
|
|
||||||
yaml, err := dao.Describe(b.app.Conn(), b.gvr, ns, n)
|
|
||||||
if err != nil {
|
|
||||||
b.app.Flash().Errf("Describe command failed: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
details := NewDetails(b.App(), "Describe", sel).Update(yaml)
|
|
||||||
if err := b.app.inject(details); err != nil {
|
|
||||||
b.app.Flash().Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
||||||
path := b.GetSelectedItem()
|
|
||||||
if path == "" {
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
|
|
||||||
o, err := b.app.factory.Get(string(b.gvr), path, labels.Everything())
|
|
||||||
if err != nil {
|
|
||||||
b.app.Flash().Errf("Unable to get resource %q -- %s", b.gvr, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := toYAML(o)
|
|
||||||
if err != nil {
|
|
||||||
b.app.Flash().Errf("Unable to marshal resource %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
details := NewDetails(b.app, "YAML", path).Update(raw)
|
|
||||||
if err := b.app.inject(details); err != nil {
|
|
||||||
b.App().Flash().Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toYAML(o runtime.Object) (string, error) {
|
|
||||||
var (
|
|
||||||
buff bytes.Buffer
|
|
||||||
p printers.YAMLPrinter
|
|
||||||
)
|
|
||||||
err := p.PrintObj(o, &buff)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Msgf("Marshal Error %v", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buff.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := b.GetSelectedItem()
|
path := b.GetSelectedItem()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
@ -388,21 +266,6 @@ func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) setNamespace(ns string) {
|
|
||||||
if !b.meta.Namespaced {
|
|
||||||
b.GetModel().SetNamespace(render.ClusterScope)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if b.GetModel().InNamespace(ns) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ns == render.NamespaceAll {
|
|
||||||
ns = render.AllNamespaces
|
|
||||||
}
|
|
||||||
b.GetModel().SetNamespace(ns)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
i, _ := strconv.Atoi(string(evt.Rune()))
|
i, _ := strconv.Atoi(string(evt.Rune()))
|
||||||
ns := b.namespaces[i]
|
ns := b.namespaces[i]
|
||||||
|
|
@ -427,6 +290,24 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Helpers...
|
||||||
|
|
||||||
|
func (b *Browser) setNamespace(ns string) {
|
||||||
|
if !b.meta.Namespaced {
|
||||||
|
b.GetModel().SetNamespace(render.ClusterScope)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.GetModel().InNamespace(ns) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ns == render.NamespaceAll {
|
||||||
|
ns = render.AllNamespaces
|
||||||
|
}
|
||||||
|
b.GetModel().SetNamespace(ns)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Browser) defaultContext() context.Context {
|
func (b *Browser) defaultContext() context.Context {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
@ -492,16 +373,43 @@ func (b *Browser) refreshActions() {
|
||||||
b.app.Menu().HydrateMenu(b.Hints())
|
b.app.Menu().HydrateMenu(b.Hints())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aliases returns all available aliases.
|
func (b *Browser) simpleDelete(selections []string, msg string) {
|
||||||
func (b *Browser) Aliases() []string {
|
dialog.ShowConfirm(b.app.Content.Pages, "Confirm Delete", msg, func() {
|
||||||
return append(b.meta.ShortNames, b.meta.SingularName, b.meta.Name)
|
b.ShowDeleted()
|
||||||
|
if len(selections) > 1 {
|
||||||
|
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
|
||||||
|
} else {
|
||||||
|
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
|
||||||
|
}
|
||||||
|
for _, sel := range selections {
|
||||||
|
if err := b.accessor.(dao.Nuker).Delete(sel, true, true); err != nil {
|
||||||
|
b.app.Flash().Errf("Delete failed with `%s", err)
|
||||||
|
} else {
|
||||||
|
b.GetTable().DeleteMark(sel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.refresh()
|
||||||
|
b.SelectRow(1, true)
|
||||||
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvFn returns an plugin env function if available.
|
func (b *Browser) resourceDelete(selections []string, msg string) {
|
||||||
func (b *Browser) EnvFn() EnvFunc {
|
dialog.ShowDelete(b.app.Content.Pages, msg, func(cascade, force bool) {
|
||||||
return b.envFn
|
b.ShowDeleted()
|
||||||
}
|
if len(selections) > 1 {
|
||||||
|
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
|
||||||
func (b *Browser) defaultK9sEnv() K9sEnv {
|
} else {
|
||||||
return defaultK9sEnv(b.app, b.GetSelectedItem(), b.GetSelectedRow())
|
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
|
||||||
|
}
|
||||||
|
for _, sel := range selections {
|
||||||
|
if err := b.accessor.(dao.Nuker).Delete(sel, cascade, force); err != nil {
|
||||||
|
b.app.Flash().Errf("Delete failed with `%s", err)
|
||||||
|
} else {
|
||||||
|
b.app.factory.DeleteForwarder(sel)
|
||||||
|
b.GetTable().DeleteMark(sel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.refresh()
|
||||||
|
b.SelectRow(1, true)
|
||||||
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -9,11 +10,42 @@ import (
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/cli-runtime/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func describeResource(app *App, _, gvr, path string) {
|
||||||
|
ns, n := client.Namespaced(path)
|
||||||
|
yaml, err := dao.Describe(app.Conn(), client.GVR(gvr), ns, n)
|
||||||
|
if err != nil {
|
||||||
|
app.Flash().Errf("Describe command failed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
details := NewDetails(app, "Describe", path).Update(yaml)
|
||||||
|
if err := app.inject(details); err != nil {
|
||||||
|
app.Flash().Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toYAML(o runtime.Object) (string, error) {
|
||||||
|
var (
|
||||||
|
buff bytes.Buffer
|
||||||
|
p printers.YAMLPrinter
|
||||||
|
)
|
||||||
|
err := p.PrintObj(o, &buff)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msgf("Marshal Error %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func showPodsWithLabels(app *App, path string, sel map[string]string) {
|
func showPodsWithLabels(app *App, path string, sel map[string]string) {
|
||||||
log.Debug().Msgf("SHOWING POD FOR %#v", sel)
|
log.Debug().Msgf("SHOWING POD FOR %#v", sel)
|
||||||
var labels []string
|
var labels []string
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,31 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/atotto/clipboard"
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Table represents a table viewer.
|
// Table represents a table viewer.
|
||||||
type Table struct {
|
type Table struct {
|
||||||
*ui.Table
|
*ui.Table
|
||||||
|
|
||||||
app *App
|
gvr client.GVR
|
||||||
enterFn EnterFunc
|
app *App
|
||||||
|
enterFn EnterFunc
|
||||||
|
envFn EnvFunc
|
||||||
|
bindKeysFn BindKeysFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new viewer.
|
// NewTable returns a new viewer.
|
||||||
func NewTable(gvr string) *Table {
|
func NewTable(gvr client.GVR) *Table {
|
||||||
return &Table{
|
return &Table{
|
||||||
Table: ui.NewTable(gvr),
|
Table: ui.NewTable(string(gvr)),
|
||||||
|
gvr: gvr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,6 +41,7 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
t.Table.Init(ctx)
|
t.Table.Init(ctx)
|
||||||
t.bindKeys()
|
t.bindKeys()
|
||||||
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
||||||
|
t.envFn = t.defaultK9sEnv
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -41,6 +49,24 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
// Name returns the table name.
|
// Name returns the table name.
|
||||||
func (t *Table) Name() string { return t.BaseTitle }
|
func (t *Table) Name() string { return t.BaseTitle }
|
||||||
|
|
||||||
|
// GVR returns a resource descriptor.
|
||||||
|
func (t *Table) GVR() string { return string(t.gvr) }
|
||||||
|
|
||||||
|
// SetBindKeysFn adds additional key bindings.
|
||||||
|
func (t *Table) SetBindKeysFn(f BindKeysFunc) { t.bindKeysFn = f }
|
||||||
|
|
||||||
|
// SetEnvFn sets a function to pull viewer env vars for plugins.
|
||||||
|
func (t *Table) SetEnvFn(f EnvFunc) { t.envFn = f }
|
||||||
|
|
||||||
|
// EnvFn returns an plugin env function if available.
|
||||||
|
func (t *Table) EnvFn() EnvFunc {
|
||||||
|
return t.envFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) defaultK9sEnv() K9sEnv {
|
||||||
|
return defaultK9sEnv(t.app, t.GetSelectedItem(), t.GetSelectedRow())
|
||||||
|
}
|
||||||
|
|
||||||
// App returns the current app handle.
|
// App returns the current app handle.
|
||||||
func (t *Table) App() *App {
|
func (t *Table) App() *App {
|
||||||
return t.app
|
return t.app
|
||||||
|
|
@ -102,6 +128,48 @@ func (t *Table) bindKeys() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Table) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
path := t.GetSelectedItem()
|
||||||
|
if path == "" {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
o, err := t.app.factory.Get(string(t.gvr), path, labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
t.app.Flash().Errf("Unable to get resource %q -- %s", t.gvr, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := toYAML(o)
|
||||||
|
if err != nil {
|
||||||
|
t.app.Flash().Errf("Unable to marshal resource %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
details := NewDetails(t.app, "YAML", path).Update(raw)
|
||||||
|
if err := t.app.inject(details); err != nil {
|
||||||
|
t.App().Flash().Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
path := t.GetSelectedItem()
|
||||||
|
if path == "" {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
_, n := client.Namespaced(path)
|
||||||
|
log.Debug().Msgf("Copied selection to clipboard %q", n)
|
||||||
|
t.app.Flash().Info("Current selection copied to clipboard...")
|
||||||
|
if err := clipboard.WriteAll(n); err != nil {
|
||||||
|
t.app.Flash().Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Table) markCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (t *Table) markCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := t.GetSelectedItem()
|
path := t.GetSelectedItem()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue