parent
4c222f80ed
commit
ba84183034
|
|
@ -1,6 +1,7 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -97,7 +98,7 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
if !resp.Status.Allowed {
|
||||
return false, err
|
||||
return false, fmt.Errorf("%s access denied for current user on %q:%s", v, ns, gvr)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ func (a *Alias) Clear() {
|
|||
|
||||
// Ensure makes sure alias are loaded.
|
||||
func (a *Alias) Ensure() (config.Alias, error) {
|
||||
if len(a.Alias) == 0 {
|
||||
if err := LoadResources(a.factory); err != nil {
|
||||
return config.Alias{}, err
|
||||
}
|
||||
return a.Alias, a.load()
|
||||
// if len(a.Alias) > 0 {
|
||||
// return a.Alias, nil
|
||||
// }
|
||||
if err := LoadResources(a.factory); err != nil {
|
||||
return config.Alias{}, err
|
||||
}
|
||||
return a.Alias, nil
|
||||
return a.Alias, a.load()
|
||||
}
|
||||
|
||||
func (a *Alias) load() error {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,10 @@ func LoadResources(f Factory) error {
|
|||
}
|
||||
loadNonResource(resMetas)
|
||||
|
||||
return loadCRDs(f, resMetas)
|
||||
if err := loadCRDs(f, resMetas); err != nil {
|
||||
log.Warn().Err(err).Msgf("CRDs load failed!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BOZO!! Need contermeasure for direct commands!
|
||||
|
|
@ -172,7 +175,7 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
|||
}
|
||||
rr, err := discovery.ServerPreferredResources()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Warn().Err(err).Msgf("Failed to load preferred resources")
|
||||
}
|
||||
for _, r := range rr {
|
||||
for _, res := range r.APIResources {
|
||||
|
|
@ -188,11 +191,13 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
|||
func loadCRDs(f Factory, m ResourceMetas) error {
|
||||
log.Debug().Msgf("Loading CRDs...")
|
||||
const crdGVR = "apiextensions.k8s.io/v1beta1/customresourcedefinitions"
|
||||
_ = f.ForResource("", crdGVR)
|
||||
f.WaitForCacheSync()
|
||||
_, err := f.CanForResource("", crdGVR, "list")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oo, err := f.List(crdGVR, "", labels.Everything())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Fail CRDs load")
|
||||
log.Warn().Err(err).Msgf("Fail CRDs load")
|
||||
return nil
|
||||
}
|
||||
log.Debug().Msgf(">>> CRDS count %d", len(oo))
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ type Factory interface {
|
|||
// ForResource fetch an informer for a given resource.
|
||||
ForResource(ns, gvr string) informers.GenericInformer
|
||||
|
||||
// CanForResource fetch an informer for a given resource if authorized
|
||||
CanForResource(ns, gvr string, verbs ...string) (informers.GenericInformer, error)
|
||||
|
||||
// WaitForCacheSync synchronize the cache.
|
||||
WaitForCacheSync()
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type CustomResourceDefinition struct {
|
|||
func (c *CustomResourceDefinition) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||
lsel := labels.Everything()
|
||||
if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil {
|
||||
if sel, e := labels.ConvertSelectorToLabelsMap(strLabel); ok && e == nil {
|
||||
lsel = sel.AsSelector()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -14,33 +16,32 @@ type HorizontalPodAutoscaler struct {
|
|||
}
|
||||
|
||||
// List returns a collection of nodes.
|
||||
func (c *HorizontalPodAutoscaler) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
func (h *HorizontalPodAutoscaler) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||
lsel := labels.Everything()
|
||||
if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil {
|
||||
lsel = sel.AsSelector()
|
||||
}
|
||||
|
||||
gvr := "autoscaling/v2beta2/horizontalpodautoscalers"
|
||||
ooV2b2, err := c.factory.List(gvr, c.namespace, lsel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ooV2b2) > 0 {
|
||||
return ooV2b2, nil
|
||||
gvrs := []string{
|
||||
"autoscaling/v2beta2/horizontalpodautoscalers",
|
||||
"autoscaling/v2beta1/horizontalpodautoscalers",
|
||||
"autoscaling/v1/horizontalpodautoscalers",
|
||||
}
|
||||
|
||||
gvr = "autoscaling/v2beta1/horizontalpodautoscalers"
|
||||
ooV2b1, err := c.factory.List(gvr, c.namespace, lsel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ooV2b1) > 0 {
|
||||
return ooV2b1, nil
|
||||
for _, gvr := range gvrs {
|
||||
oo, err := h.list(gvr, lsel)
|
||||
if err == nil && len(oo) > 0 {
|
||||
return oo, nil
|
||||
}
|
||||
}
|
||||
log.Error().Err(fmt.Errorf("No results for any known HPA versions"))
|
||||
|
||||
gvr = "autoscaling/v1/horizontalpodautoscalers"
|
||||
oo, err := c.factory.List(gvr, c.namespace, lsel)
|
||||
return []runtime.Object{}, nil
|
||||
}
|
||||
|
||||
func (h *HorizontalPodAutoscaler) list(gvr string, sel labels.Selector) ([]runtime.Object, error) {
|
||||
oo, err := h.factory.List(gvr, h.namespace, sel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,9 @@ func (p *Policy) fetchRoleBindingSubjects(kind, name string) ([]string, error) {
|
|||
}
|
||||
|
||||
func (p *Policy) fetchClusterRoles() ([]rbacv1.ClusterRole, error) {
|
||||
oo, err := p.factory.List("rbac.authorization.k8s.io/v1/clusterroles", render.ClusterScope, labels.Everything())
|
||||
const gvr = "rbac.authorization.k8s.io/v1/clusterroles"
|
||||
|
||||
oo, err := p.factory.List(gvr, render.ClusterScope, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -189,7 +191,9 @@ func (p *Policy) fetchClusterRoles() ([]rbacv1.ClusterRole, error) {
|
|||
}
|
||||
|
||||
func (p *Policy) fetchRoles() ([]rbacv1.Role, error) {
|
||||
oo, err := p.factory.List("rbac.authorization.k8s.io/v1/roles", render.AllNamespaces, labels.Everything())
|
||||
const gvr = "rbac.authorization.k8s.io/v1/roles"
|
||||
|
||||
oo, err := p.factory.List(gvr, render.AllNamespaces, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -22,11 +23,13 @@ func (r *Resource) Init(ns, gvr string, f Factory) {
|
|||
|
||||
// List returns a collection of nodes.
|
||||
func (r *Resource) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
log.Debug().Msgf("MODELLIST %q:%q", r.namespace, r.gvr)
|
||||
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||
lsel := labels.Everything()
|
||||
if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil {
|
||||
lsel = sel.AsSelector()
|
||||
}
|
||||
|
||||
return r.factory.List(r.gvr, r.namespace, lsel)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
refreshRate = 2 * time.Second
|
||||
refreshRate = 1 * time.Second
|
||||
noDataCount = 2
|
||||
)
|
||||
|
||||
|
|
@ -115,6 +115,7 @@ func (t *Table) refresh(ctx context.Context) {
|
|||
if err := t.reconcile(ctx); err != nil {
|
||||
log.Error().Err(err).Msg("Reconcile failed")
|
||||
t.fireTableLoadFailed(err)
|
||||
return
|
||||
}
|
||||
t.fireTableChanged(*t.data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
const (
|
||||
benchFmat = "%s_%s_%d.txt"
|
||||
k9sUA = "k9s/0.0.7"
|
||||
k9sUA = "k9s/"
|
||||
)
|
||||
|
||||
// K9sBenchDir directory to store K9s Benchmark files.
|
||||
|
|
@ -32,15 +32,15 @@ type Benchmark struct {
|
|||
}
|
||||
|
||||
// NewBenchmark returns a new benchmark.
|
||||
func NewBenchmark(base string, cfg config.BenchConfig) (*Benchmark, error) {
|
||||
func NewBenchmark(base, version string, cfg config.BenchConfig) (*Benchmark, error) {
|
||||
b := Benchmark{config: cfg}
|
||||
if err := b.init(base); err != nil {
|
||||
if err := b.init(base, version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
func (b *Benchmark) init(base string) error {
|
||||
func (b *Benchmark) init(base, version string) error {
|
||||
req, err := http.NewRequest(b.config.HTTP.Method, base, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -58,6 +58,7 @@ func (b *Benchmark) init(base string) error {
|
|||
} else {
|
||||
ua += " " + k9sUA
|
||||
}
|
||||
ua += version
|
||||
if req.Header == nil {
|
||||
req.Header = make(http.Header)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
)
|
||||
|
||||
|
|
@ -48,30 +49,25 @@ func (g *Generic) Render(o interface{}, ns string, r *Row) error {
|
|||
return fmt.Errorf("expecting a TableRow but got %T", o)
|
||||
}
|
||||
|
||||
count := len(row.Cells)
|
||||
if ns == AllNamespaces {
|
||||
count++
|
||||
}
|
||||
r.ID, ok = row.Cells[0].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting row id to be a string but got %#v", row.Cells[0])
|
||||
}
|
||||
|
||||
r.Fields = make(Fields, count)
|
||||
var index int
|
||||
if ns == AllNamespaces {
|
||||
rns, err := extractNamespace(row.Object.Raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Fields[index] = rns
|
||||
r.ID = FQN(rns, r.ID)
|
||||
index++
|
||||
rns, err := extractNamespace(row.Object.Raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.ID = FQN(rns, r.ID)
|
||||
r.Fields = make(Fields, 0, len(g.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
r.Fields = append(r.Fields, rns)
|
||||
}
|
||||
for _, c := range row.Cells {
|
||||
r.Fields[index] = fmt.Sprintf("%v", c)
|
||||
index++
|
||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", c))
|
||||
}
|
||||
log.Debug().Msgf("GENERIC %#v", r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func TestGenericRender(t *testing.T) {
|
|||
row := makeGeneric().Rows[0]
|
||||
assert.Nil(t, g.Render(&row, "blee", &r))
|
||||
|
||||
assert.Equal(t, "a", r.ID)
|
||||
assert.Equal(t, "blee/a", r.ID)
|
||||
assert.Equal(t, render.Fields{"a", "b", "c"}, r.Fields)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,9 +306,11 @@ func (a *App) Status(l ui.FlashLevel, msg string) {
|
|||
}
|
||||
|
||||
// ClearStatus reset log back to normal.
|
||||
func (a *App) ClearStatus() {
|
||||
func (a *App) ClearStatus(flash bool) {
|
||||
a.Logo().Reset()
|
||||
a.Flash().Clear()
|
||||
if flash {
|
||||
a.Flash().Clear()
|
||||
}
|
||||
a.Draw()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/ui/dialog"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -133,7 +134,7 @@ func (b *Browser) Aliases() []string {
|
|||
func (b *Browser) TableLoadFailed(err error) {
|
||||
b.app.QueueUpdateDraw(func() {
|
||||
b.app.Flash().Err(err)
|
||||
b.App().ClearStatus()
|
||||
b.App().ClearStatus(false)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +143,7 @@ func (b *Browser) TableDataChanged(data render.TableData) {
|
|||
b.app.QueueUpdateDraw(func() {
|
||||
b.refreshActions()
|
||||
b.Update(data)
|
||||
b.App().ClearStatus()
|
||||
b.App().ClearStatus(false)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +274,12 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
ns = render.NamespaceAll
|
||||
}
|
||||
|
||||
auth, err := b.App().factory.Client().CanI(ns, b.GVR(), watch.ReadVerbs)
|
||||
if !auth {
|
||||
b.App().Flash().Err(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
b.app.switchNS(ns)
|
||||
b.setNamespace(ns)
|
||||
b.app.Flash().Infof("Viewing namespace `%s`...", ns)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var customViewers MetaViewers
|
||||
var (
|
||||
customViewers MetaViewers
|
||||
|
||||
canRX = regexp.MustCompile(`\Acan\s([u|g|s]):([\w-:]+)\b`)
|
||||
)
|
||||
|
||||
// Command represents a user command.
|
||||
type Command struct {
|
||||
|
|
@ -38,6 +42,37 @@ func (c *Command) Init() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Exec the Command by showing associated display.
|
||||
func (c *Command) run(cmd string, clearStack bool) error {
|
||||
if c.specialCmd(cmd) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmds := strings.Split(cmd, " ")
|
||||
gvr, v, err := c.viewMetaFor(cmds[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch cmds[0] {
|
||||
case "ctx", "context", "contexts":
|
||||
if len(cmds) == 2 && c.app.switchCtx(cmds[1], true) != nil {
|
||||
return fmt.Errorf("context switch failed!")
|
||||
}
|
||||
view := c.componentFor(gvr, v)
|
||||
return c.exec(gvr, view, clearStack)
|
||||
default:
|
||||
// checks if Command includes a namespace
|
||||
ns := c.app.Config.ActiveNamespace()
|
||||
if len(cmds) == 2 {
|
||||
ns = cmds[1]
|
||||
}
|
||||
if !c.app.switchNS(ns) {
|
||||
return fmt.Errorf("namespace switch failed for ns %q", ns)
|
||||
}
|
||||
return c.exec(gvr, c.componentFor(gvr, v), clearStack)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets Command and reload aliases.
|
||||
func (c *Command) Reset() error {
|
||||
c.alias.Clear()
|
||||
|
|
@ -52,8 +87,6 @@ func (c *Command) defaultCmd() error {
|
|||
return c.run(c.app.Config.ActiveView(), true)
|
||||
}
|
||||
|
||||
var canRX = regexp.MustCompile(`\Acan\s([u|g|s]):([\w-:]+)\b`)
|
||||
|
||||
func (c *Command) specialCmd(cmd string) bool {
|
||||
cmds := strings.Split(cmd, " ")
|
||||
switch cmds[0] {
|
||||
|
|
@ -96,37 +129,6 @@ func (c *Command) viewMetaFor(cmd string) (string, *MetaViewer, error) {
|
|||
return gvr, &v, nil
|
||||
}
|
||||
|
||||
// Exec the Command by showing associated display.
|
||||
func (c *Command) run(cmd string, clearStack bool) error {
|
||||
if c.specialCmd(cmd) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmds := strings.Split(cmd, " ")
|
||||
gvr, v, err := c.viewMetaFor(cmds[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch cmds[0] {
|
||||
case "ctx", "context", "contexts":
|
||||
if len(cmds) == 2 && c.app.switchCtx(cmds[1], true) != nil {
|
||||
return fmt.Errorf("context switch failed!")
|
||||
}
|
||||
view := c.componentFor(gvr, v)
|
||||
return c.exec(gvr, view, clearStack)
|
||||
default:
|
||||
// checks if Command includes a namespace
|
||||
ns := c.app.Config.ActiveNamespace()
|
||||
if len(cmds) == 2 {
|
||||
ns = cmds[1]
|
||||
}
|
||||
if !c.app.switchNS(ns) {
|
||||
return fmt.Errorf("namespace switch failed for ns %q", ns)
|
||||
}
|
||||
return c.exec(gvr, c.componentFor(gvr, v), clearStack)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Command) componentFor(gvr string, v *MetaViewer) ResourceViewer {
|
||||
var view ResourceViewer
|
||||
if v.viewerFn != nil {
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ func (v *Help) showGeneral() model.MenuHints {
|
|||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-r",
|
||||
Description: "Refresh",
|
||||
Description: "Reload",
|
||||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-u",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ func toYAML(o runtime.Object) (string, error) {
|
|||
}
|
||||
|
||||
func showPodsWithLabels(app *App, path string, sel map[string]string) {
|
||||
log.Debug().Msgf("SHOWING POD FOR %#v", sel)
|
||||
var labels []string
|
||||
for k, v := range sel {
|
||||
labels = append(labels, fmt.Sprintf("%s=%s", k, v))
|
||||
|
|
|
|||
|
|
@ -131,12 +131,8 @@ func (l *Log) bindKeys() {
|
|||
tcell.KeyEscape: ui.NewKeyAction("Back", l.app.PrevCmd, true),
|
||||
ui.KeyC: ui.NewKeyAction("Clear", l.clearCmd, true),
|
||||
ui.KeyS: ui.NewKeyAction("Toggle AutoScroll", l.toggleAutoScrollCmd, true),
|
||||
ui.KeyG: ui.NewKeyAction("Top", l.topCmd, false),
|
||||
ui.KeyShiftF: ui.NewKeyAction("FullScreen", l.fullScreenCmd, true),
|
||||
ui.KeyF: ui.NewKeyAction("FullScreen", l.fullScreenCmd, true),
|
||||
ui.KeyW: ui.NewKeyAction("Toggle Wrap", l.textWrapCmd, true),
|
||||
ui.KeyShiftG: ui.NewKeyAction("Bottom", l.bottomCmd, false),
|
||||
ui.KeyF: ui.NewKeyAction("Up", l.pageUpCmd, false),
|
||||
ui.KeyB: ui.NewKeyAction("Down", l.pageDownCmd, false),
|
||||
tcell.KeyCtrlS: ui.NewKeyAction("Save", l.SaveCmd, true),
|
||||
})
|
||||
}
|
||||
|
|
@ -306,32 +302,6 @@ func saveData(cluster, name, data string) (string, error) {
|
|||
return path, nil
|
||||
}
|
||||
|
||||
func (l *Log) topCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
l.app.Flash().Info("Top of logs...")
|
||||
l.logs.ScrollToBeginning()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) bottomCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
l.app.Flash().Info("Bottom of logs...")
|
||||
l.logs.ScrollToEnd()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) pageUpCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
if l.logs.PageUp() {
|
||||
l.app.Flash().Info("Reached Top ...")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) pageDownCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
if l.logs.PageDown() {
|
||||
l.app.Flash().Info("Reached Bottom ...")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
l.app.Flash().Info("Clearing logs...")
|
||||
l.logs.Clear()
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func TestLogFlush(t *testing.T) {
|
|||
assert.Equal(t, " Autoscroll: Off FullScreen: Off Wrap: Off ", v.Indicator().GetText(true))
|
||||
v.toggleAutoScrollCmd(nil)
|
||||
assert.Equal(t, " Autoscroll: On FullScreen: Off Wrap: Off ", v.Indicator().GetText(true))
|
||||
assert.Equal(t, 10, len(v.Hints()))
|
||||
assert.Equal(t, 6, len(v.Hints()))
|
||||
}
|
||||
|
||||
func TestLogViewSave(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package view
|
|||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
|
@ -55,6 +57,15 @@ func isResourcePath(p string) bool {
|
|||
|
||||
func (l *LogsExtender) showLogs(path string, prev bool) {
|
||||
log.Debug().Msgf("SHOWING LOGS path %q", path)
|
||||
// Need to load and wait for pods
|
||||
ns, _ := render.Namespaced(path)
|
||||
_, err := l.App().factory.CanForResource(ns, "v1/pods", watch.ReadVerbs...)
|
||||
if err != nil {
|
||||
l.App().Flash().Err(err)
|
||||
return
|
||||
}
|
||||
l.App().factory.WaitForCacheSync()
|
||||
|
||||
co := ""
|
||||
if l.containerFn != nil {
|
||||
co = l.containerFn()
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ func (p *PortForward) portForwardContext(ctx context.Context) context.Context {
|
|||
func (p *PortForward) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyEnter: ui.NewKeyAction("Benchmarks", p.showBenchCmd, true),
|
||||
tcell.KeyCtrlB: ui.NewKeyAction("Bench", p.benchCmd, true),
|
||||
tcell.KeyCtrlK: ui.NewKeyAction("Bench Stop", p.benchStopCmd, true),
|
||||
ui.KeyB: ui.NewKeyAction("Bench", p.benchCmd, true),
|
||||
ui.KeyK: ui.NewKeyAction("Bench Stop", p.benchStopCmd, true),
|
||||
tcell.KeyCtrlD: ui.NewKeyAction("Delete", p.deleteCmd, true),
|
||||
ui.KeyShiftP: ui.NewKeyAction("Sort Ports", p.GetTable().SortColCmd(2, true), false),
|
||||
ui.KeyShiftU: ui.NewKeyAction("Sort URL", p.GetTable().SortColCmd(4, true), false),
|
||||
|
|
@ -70,7 +70,7 @@ func (p *PortForward) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
p.App().Status(ui.FlashErr, "Benchmark Camceled!")
|
||||
p.bench.Cancel()
|
||||
}
|
||||
p.App().ClearStatus()
|
||||
p.App().ClearStatus(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -95,9 +95,9 @@ func (p *PortForward) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
base := ui.TrimCell(p.GetTable().SelectTable, r, 4)
|
||||
var err error
|
||||
if p.bench, err = perf.NewBenchmark(base, cfg); err != nil {
|
||||
if p.bench, err = perf.NewBenchmark(base, p.App().version, cfg); err != nil {
|
||||
p.App().Flash().Errf("Bench failed %v", err)
|
||||
p.App().ClearStatus()
|
||||
p.App().ClearStatus(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ func (p *PortForward) runBenchmark() {
|
|||
p.bench = nil
|
||||
go func() {
|
||||
<-time.After(2 * time.Second)
|
||||
p.App().QueueUpdate(func() { p.App().ClearStatus() })
|
||||
p.App().QueueUpdate(func() { p.App().ClearStatus(true) })
|
||||
}()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,19 +32,19 @@ func NewReplicaSet(gvr client.GVR) ResourceViewer {
|
|||
r := ReplicaSet{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
r.bindKeys()
|
||||
r.SetBindKeysFn(r.bindKeys)
|
||||
r.GetTable().SetEnterFn(r.showPods)
|
||||
r.GetTable().SetColorerFn(render.ReplicaSet{}.ColorerFunc())
|
||||
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r *ReplicaSet) bindKeys() {
|
||||
r.Actions().Add(ui.KeyActions{
|
||||
func (r *ReplicaSet) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyShiftD: ui.NewKeyAction("Sort Desired", r.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort Current", r.GetTable().SortColCmd(2, true), false),
|
||||
ui.KeyShiftR: ui.NewKeyAction("Sort Ready", r.GetTable().SortColCmd(3, true), false),
|
||||
tcell.KeyCtrlB: ui.NewKeyAction("Rollback", r.rollbackCmd, true),
|
||||
tcell.KeyCtrlL: ui.NewKeyAction("Rollback", r.rollbackCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,17 +40,26 @@ func (s *StatefulSet) bindKeys(aa ui.KeyActions) {
|
|||
}
|
||||
|
||||
func (s *StatefulSet) showPods(app *App, _, gvr, path string) {
|
||||
o, err := app.factory.Get(s.GVR(), path, labels.Everything())
|
||||
sts, err := s.sts(path)
|
||||
if err != nil {
|
||||
app.Flash().Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), sts.Spec.Selector)
|
||||
}
|
||||
|
||||
func (s *StatefulSet) sts(path string) (*appsv1.StatefulSet, error) {
|
||||
o, err := s.App().factory.Get(s.GVR(), path, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sts appsv1.StatefulSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
|
||||
if err != nil {
|
||||
app.Flash().Err(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), sts.Spec.Selector)
|
||||
return &sts, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,14 +40,13 @@ func NewService(gvr client.GVR) ResourceViewer {
|
|||
|
||||
func (s *Service) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyCtrlB: ui.NewKeyAction("Bench", s.benchCmd, true),
|
||||
tcell.KeyCtrlK: ui.NewKeyAction("Bench Stop", s.benchStopCmd, true),
|
||||
ui.KeyShiftT: ui.NewKeyAction("Sort Type", s.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyB: ui.NewKeyAction("Bench", s.benchCmd, true),
|
||||
ui.KeyK: ui.NewKeyAction("Bench Stop", s.benchStopCmd, true),
|
||||
ui.KeyShiftT: ui.NewKeyAction("Sort Type", s.GetTable().SortColCmd(1, true), false),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) showPods(app *App, ns, gvr, path string) {
|
||||
log.Debug().Msgf("SVC SHOW PODS %q", path)
|
||||
o, err := app.factory.Get(gvr, path, labels.Everything())
|
||||
if err != nil {
|
||||
app.Flash().Err(err)
|
||||
|
|
@ -61,7 +60,7 @@ func (s *Service) showPods(app *App, ns, gvr, path string) {
|
|||
return
|
||||
}
|
||||
|
||||
showPodsWithLabels(app, path, svc.Spec.Selector)
|
||||
showPodsWithLabels(app, strings.Replace(path, "/", "::", 1), svc.Spec.Selector)
|
||||
}
|
||||
|
||||
func (s *Service) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
@ -70,7 +69,7 @@ func (s *Service) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
s.App().Status(ui.FlashErr, "Benchmark Canceled!")
|
||||
s.bench.Cancel()
|
||||
}
|
||||
s.App().ClearStatus()
|
||||
s.App().ClearStatus(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -136,7 +135,7 @@ func (s *Service) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
if err := s.runBenchmark(port, cfg); err != nil {
|
||||
s.App().Flash().Errf("Benchmark failed %v", err)
|
||||
s.App().ClearStatus()
|
||||
s.App().ClearStatus(false)
|
||||
s.bench = nil
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +150,7 @@ func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
|
|||
|
||||
var err error
|
||||
base := "http://" + cfg.HTTP.Host + ":" + port + cfg.HTTP.Path
|
||||
if s.bench, err = perf.NewBenchmark(base, cfg); err != nil {
|
||||
if s.bench, err = perf.NewBenchmark(base, s.App().version, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +178,6 @@ func (s *Service) benchDone() {
|
|||
func benchTimedOut(app *App) {
|
||||
<-time.After(2 * time.Second)
|
||||
app.QueueUpdate(func() {
|
||||
app.ClearStatus()
|
||||
app.ClearStatus(true)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ const (
|
|||
clusterScope = "-"
|
||||
)
|
||||
|
||||
var ReadVerbs = []string{"get", "list", "watch"}
|
||||
|
||||
// Factory tracks various resource informers.
|
||||
type Factory struct {
|
||||
factories map[string]di.DynamicSharedInformerFactory
|
||||
|
|
@ -37,6 +39,7 @@ func NewFactory(client client.Connection) *Factory {
|
|||
|
||||
// Start initializes the informers until caller cancels the context.
|
||||
func (f *Factory) Start(ns string) {
|
||||
log.Debug().Msgf("Starting factory in ns `%q", ns)
|
||||
f.stopChan = make(chan struct{})
|
||||
for ns, fac := range f.factories {
|
||||
log.Debug().Msgf("Starting factory in ns %q", ns)
|
||||
|
|
@ -62,10 +65,10 @@ func (f *Factory) List(gvr, ns string, sel labels.Selector) ([]runtime.Object, e
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug().Msgf("LISTING %q:%q", ns, gvr)
|
||||
if ns == clusterScope {
|
||||
return inf.Lister().List(sel)
|
||||
}
|
||||
|
||||
return inf.Lister().ByNamespace(ns).List(sel)
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +79,8 @@ func (f *Factory) Get(gvr, path string, sel labels.Selector) (runtime.Object, er
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug().Msgf("GETTING %q:%q", ns, gvr)
|
||||
|
||||
if ns == clusterScope {
|
||||
return inf.Lister().Get(n)
|
||||
}
|
||||
|
|
@ -117,8 +122,7 @@ func (f *Factory) isClusterWide() bool {
|
|||
}
|
||||
|
||||
func (f *Factory) preload(_ string) {
|
||||
verbs := []string{"get", "list", "watch"}
|
||||
_, _ = f.CanForResource("", "apiextensions.k8s.io/v1beta1/customresourcedefinitions", verbs...)
|
||||
_, _ = f.CanForResource("", "apiextensions.k8s.io/v1beta1/customresourcedefinitions", ReadVerbs...)
|
||||
// BOZO!!
|
||||
// _, _ = f.CanForResource(ns, "v1/pods", verbs...)
|
||||
// _, _ = f.CanForResource(clusterScope, "rbac.authorization.k8s.io/v1/clusterroles", verbs...)
|
||||
|
|
@ -148,9 +152,6 @@ func (f *Factory) ForResource(ns, gvr string) informers.GenericInformer {
|
|||
}
|
||||
|
||||
func (f *Factory) ensureFactory(ns string) di.DynamicSharedInformerFactory {
|
||||
if f.isClusterWide() {
|
||||
ns = allNamespaces
|
||||
}
|
||||
if fac, ok := f.factories[ns]; ok {
|
||||
return fac
|
||||
}
|
||||
|
|
@ -173,12 +174,9 @@ func (f *Factory) AddForwarder(pf Forwarder) {
|
|||
|
||||
// DeleteForwarder deletes portforward for a given container.
|
||||
func (f *Factory) DeleteForwarder(path string) {
|
||||
fwd, ok := f.forwarders[path]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
fwd.Stop()
|
||||
delete(f.forwarders, path)
|
||||
f.forwarders.Dump()
|
||||
count := f.forwarders.Kill(path)
|
||||
log.Warn().Msgf("Deleted (%d) portforward for %q", count, path)
|
||||
}
|
||||
|
||||
// Forwarders returns all portforwards.
|
||||
|
|
|
|||
Loading…
Reference in New Issue