diff --git a/internal/dao/registry.go b/internal/dao/registry.go index 2becdf19..74e8d2ab 100644 --- a/internal/dao/registry.go +++ b/internal/dao/registry.go @@ -87,20 +87,17 @@ func IsK9sMeta(m metav1.APIResource) bool { // Load hydrates server preferred+CRDs resource metadata. func LoadResources(f Factory) error { - log.Debug().Msgf("LOAD RES") resMetas = make(ResourceMetas, 100) if err := loadPreferred(f, resMetas); err != nil { return err } - if err := loadNonResource(resMetas); err != nil { - return err - } + loadNonResource(resMetas) return loadCRDs(f, resMetas) } // BOZO!! Need contermeasure for direct commands! -func loadNonResource(m ResourceMetas) error { +func loadNonResource(m ResourceMetas) { m["aliases"] = metav1.APIResource{ Name: "aliases", Kind: "Aliases", @@ -134,6 +131,16 @@ func loadNonResource(m ResourceMetas) error { Verbs: []string{"delete"}, Categories: []string{"k9s"}, } + m["containers"] = metav1.APIResource{ + Name: "containers", + Kind: "Containers", + Categories: []string{"k9s"}, + } + + loadRBAC(m) +} + +func loadRBAC(m ResourceMetas) { m["rbac"] = metav1.APIResource{ Name: "rbacs", Kind: "Rules", @@ -145,11 +152,6 @@ func loadNonResource(m ResourceMetas) error { Namespaced: true, Categories: []string{"k9s"}, } - m["containers"] = metav1.APIResource{ - Name: "containers", - Kind: "Containers", - Categories: []string{"k9s"}, - } m["users"] = metav1.APIResource{ Name: "users", Kind: "User", @@ -160,8 +162,6 @@ func loadNonResource(m ResourceMetas) error { Kind: "Group", Categories: []string{"k9s"}, } - - return nil } func loadPreferred(f Factory, m ResourceMetas) error { @@ -221,8 +221,8 @@ func extractMeta(o runtime.Object) (metav1.APIResource, []error) { var meta map[string]interface{} meta, errs = extractMap(crd.Object, "metadata", errs) - m.Name, errs = extractStr(meta, "name", errs) + m.Group, errs = extractStr(spec, "group", errs) m.Version, errs = extractStr(spec, "version", errs) diff --git a/internal/keys.go b/internal/keys.go index 63825175..d82167cb 100644 --- a/internal/keys.go +++ b/internal/keys.go @@ -6,19 +6,19 @@ type ContextKey string // A collection of context keys. const ( KeyFactory ContextKey = "factory" - KeyLabels = "labels" - KeyFields = "fields" - KeyTable = "table" - KeyDir = "dir" - KeyPath = "path" - KeySubject = "subject" - KeyGVR = "gvr" - KeyForwards = "forwards" - KeyContainers = "containers" - KeyBenchCfg = "benchcfg" - KeyAliases = "aliases" - KeyUID = "uid" - KeySubjectKind = "subjectKind" - KeySubjectName = "subjectName" - KeyNamespace = "namespace" + KeyLabels ContextKey = "labels" + KeyFields ContextKey = "fields" + KeyTable ContextKey = "table" + KeyDir ContextKey = "dir" + KeyPath ContextKey = "path" + KeySubject ContextKey = "subject" + KeyGVR ContextKey = "gvr" + KeyForwards ContextKey = "forwards" + KeyContainers ContextKey = "containers" + KeyBenchCfg ContextKey = "benchcfg" + KeyAliases ContextKey = "aliases" + KeyUID ContextKey = "uid" + KeySubjectKind ContextKey = "subjectKind" + KeySubjectName ContextKey = "subjectName" + KeyNamespace ContextKey = "namespace" ) diff --git a/internal/model/job.go b/internal/model/job.go index b507f5e8..bbde5a77 100644 --- a/internal/model/job.go +++ b/internal/model/job.go @@ -60,12 +60,6 @@ func (c *Job) List(ctx context.Context) ([]runtime.Object, error) { // ---------------------------------------------------------------------------- // Helpers... -func isControlledBy(cuid, id string) bool { - tokens := strings.Split(cuid, "-") - root := strings.Join(tokens[2:], "-") - return strings.Contains(id, root) -} - func isNamedAfter(p, n string) bool { tokens := strings.Split(n, "-") if len(tokens) == 0 || tokens[0] != p { diff --git a/internal/model/node.go b/internal/model/node.go index f93d1d4f..2df9ae80 100644 --- a/internal/model/node.go +++ b/internal/model/node.go @@ -30,8 +30,8 @@ func (n *Node) List(_ context.Context) ([]runtime.Object, error) { } oo := make([]runtime.Object, len(nn.Items)) - for i, n := range nn.Items { - o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&n) + for i := range nn.Items { + o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&nn.Items[i]) if err != nil { return nil, err } diff --git a/internal/model/subject.go b/internal/model/subject.go index cdf82e2d..eb4b779c 100644 --- a/internal/model/subject.go +++ b/internal/model/subject.go @@ -21,10 +21,25 @@ func (s *Subject) List(ctx context.Context) ([]runtime.Object, error) { return nil, errors.New("expecting a SubjectKind") } + crbs, err := s.listClusterRoleBindings(kind) + if err != nil { + return nil, err + } + + rbs, err := s.listRoleBindings(kind) + if err != nil { + return nil, err + } + + return append(crbs, rbs...), nil +} + +func (s *Subject) listClusterRoleBindings(kind string) ([]runtime.Object, error) { crbs, err := fetchClusterRoleBindings(s.factory) if err != nil { return nil, err } + oo := make([]runtime.Object, 0, len(crbs)) for _, crb := range crbs { for _, su := range crb.Subjects { @@ -39,10 +54,16 @@ func (s *Subject) List(ctx context.Context) ([]runtime.Object, error) { } } + return oo, nil +} + +func (s *Subject) listRoleBindings(kind string) ([]runtime.Object, error) { rbs, err := fetchRoleBindings(s.factory) if err != nil { return nil, err } + + oo := make([]runtime.Object, 0, len(rbs)) for _, rb := range rbs { for _, su := range rb.Subjects { if su.Kind != kind || inSubjectRes(oo, su.Name) { diff --git a/internal/model/table.go b/internal/model/table.go index 6e4283bf..834dc237 100644 --- a/internal/model/table.go +++ b/internal/model/table.go @@ -34,8 +34,8 @@ func NewTable(gvr string) *Table { } } -// Start initiates model updates. -func (t *Table) Start(ctx context.Context) { +// Watch initiates model updates. +func (t *Table) Watch(ctx context.Context) { t.Refresh(ctx) go t.updater(ctx) } diff --git a/internal/ui/cmd.go b/internal/ui/cmd.go index 7c816f59..326890e0 100644 --- a/internal/ui/cmd.go +++ b/internal/ui/cmd.go @@ -57,10 +57,6 @@ func (v *CmdView) write(s string) { fmt.Fprintf(v, defaultPrompt, v.icon, s) } -func (v *CmdView) reset() { - v.update("") -} - // ---------------------------------------------------------------------------- // Event Listener protocol... diff --git a/internal/ui/select_table.go b/internal/ui/select_table.go index 441ea00e..b13e971c 100644 --- a/internal/ui/select_table.go +++ b/internal/ui/select_table.go @@ -10,16 +10,39 @@ import ( "github.com/gdamore/tcell" ) -type Tabular interface { - Empty() bool - Peek() render.TableData +// Namespaceable represents a namespaceable model. +type Namespaceable interface { + // ClusterWide returns true if the model represents resource in all namespaces. ClusterWide() bool + + // GetNamespace returns the model namespace. GetNamespace() string + + // SetNamespace changes the model namespace. SetNamespace(string) - AddListener(model.TableListener) - Start(context.Context) + + // InNamespace check if current namespace matches models. InNamespace(string) bool +} + +// Tabular represents a tabular model. +type Tabular interface { + Namespaceable + + // Empty returns true if model has no data. + Empty() bool + + // Peek returns current model data. + Peek() render.TableData + + // Watch watches a given resource for changes. + Watch(context.Context) + + // SetRefreshRate sets the model watch loop rate. SetRefreshRate(time.Duration) + + // AddListener registers a model listener. + AddListener(model.TableListener) } // Selectable represents a table with selections. diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 46790d00..a8b815ad 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -67,7 +67,7 @@ func (t *testModel) ClusterWide() bool { return false } func (t *testModel) GetNamespace() string { return "blee" } func (t *testModel) SetNamespace(string) {} func (t *testModel) AddListener(model.TableListener) {} -func (t *testModel) Start(context.Context) {} +func (t *testModel) Watch(context.Context) {} func (t *testModel) InNamespace(string) bool { return true } func (t *testModel) SetRefreshRate(time.Duration) {} diff --git a/internal/view/alias_test.go b/internal/view/alias_test.go index ed109f56..a1b23edd 100644 --- a/internal/view/alias_test.go +++ b/internal/view/alias_test.go @@ -104,7 +104,7 @@ func (t *testModel) ClusterWide() bool { return false } func (t *testModel) GetNamespace() string { return "blee" } func (t *testModel) SetNamespace(string) {} func (t *testModel) AddListener(model.TableListener) {} -func (t *testModel) Start(context.Context) {} +func (t *testModel) Watch(context.Context) {} func (t *testModel) InNamespace(string) bool { return true } func (t *testModel) SetRefreshRate(time.Duration) {} diff --git a/internal/view/browser.go b/internal/view/browser.go index 6145fb8f..2993d534 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -63,8 +63,8 @@ func (b *Browser) Init(ctx context.Context) error { return err } if !dao.IsK9sMeta(b.meta) { - if _, err := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR()); err != nil { - return err + if _, e := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR()); e != nil { + return e } } @@ -105,8 +105,7 @@ func (b *Browser) Start() { if path, ok := ctx.Value(internal.KeyPath).(string); ok && path != "" { b.Path = path } - - b.GetModel().Start(ctx) + b.GetModel().Watch(ctx) } // Stop terminates browser updates. diff --git a/internal/view/table_int_test.go b/internal/view/table_int_test.go index a8ee838f..743aee96 100644 --- a/internal/view/table_int_test.go +++ b/internal/view/table_int_test.go @@ -96,7 +96,7 @@ func (t *testTableModel) ClusterWide() bool { return false } func (t *testTableModel) GetNamespace() string { return "blee" } func (t *testTableModel) SetNamespace(string) {} func (t *testTableModel) AddListener(model.TableListener) {} -func (t *testTableModel) Start(context.Context) {} +func (t *testTableModel) Watch(context.Context) {} func (t *testTableModel) InNamespace(string) bool { return true } func (t *testTableModel) SetRefreshRate(time.Duration) {}