checkpoint
parent
c26c80e170
commit
9d23488ff5
|
|
@ -87,20 +87,17 @@ func IsK9sMeta(m metav1.APIResource) bool {
|
||||||
|
|
||||||
// Load hydrates server preferred+CRDs resource metadata.
|
// Load hydrates server preferred+CRDs resource metadata.
|
||||||
func LoadResources(f Factory) error {
|
func LoadResources(f Factory) error {
|
||||||
log.Debug().Msgf("LOAD RES")
|
|
||||||
resMetas = make(ResourceMetas, 100)
|
resMetas = make(ResourceMetas, 100)
|
||||||
if err := loadPreferred(f, resMetas); err != nil {
|
if err := loadPreferred(f, resMetas); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := loadNonResource(resMetas); err != nil {
|
loadNonResource(resMetas)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadCRDs(f, resMetas)
|
return loadCRDs(f, resMetas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOZO!! Need contermeasure for direct commands!
|
// BOZO!! Need contermeasure for direct commands!
|
||||||
func loadNonResource(m ResourceMetas) error {
|
func loadNonResource(m ResourceMetas) {
|
||||||
m["aliases"] = metav1.APIResource{
|
m["aliases"] = metav1.APIResource{
|
||||||
Name: "aliases",
|
Name: "aliases",
|
||||||
Kind: "Aliases",
|
Kind: "Aliases",
|
||||||
|
|
@ -134,6 +131,16 @@ func loadNonResource(m ResourceMetas) error {
|
||||||
Verbs: []string{"delete"},
|
Verbs: []string{"delete"},
|
||||||
Categories: []string{"k9s"},
|
Categories: []string{"k9s"},
|
||||||
}
|
}
|
||||||
|
m["containers"] = metav1.APIResource{
|
||||||
|
Name: "containers",
|
||||||
|
Kind: "Containers",
|
||||||
|
Categories: []string{"k9s"},
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRBAC(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRBAC(m ResourceMetas) {
|
||||||
m["rbac"] = metav1.APIResource{
|
m["rbac"] = metav1.APIResource{
|
||||||
Name: "rbacs",
|
Name: "rbacs",
|
||||||
Kind: "Rules",
|
Kind: "Rules",
|
||||||
|
|
@ -145,11 +152,6 @@ func loadNonResource(m ResourceMetas) error {
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
Categories: []string{"k9s"},
|
Categories: []string{"k9s"},
|
||||||
}
|
}
|
||||||
m["containers"] = metav1.APIResource{
|
|
||||||
Name: "containers",
|
|
||||||
Kind: "Containers",
|
|
||||||
Categories: []string{"k9s"},
|
|
||||||
}
|
|
||||||
m["users"] = metav1.APIResource{
|
m["users"] = metav1.APIResource{
|
||||||
Name: "users",
|
Name: "users",
|
||||||
Kind: "User",
|
Kind: "User",
|
||||||
|
|
@ -160,8 +162,6 @@ func loadNonResource(m ResourceMetas) error {
|
||||||
Kind: "Group",
|
Kind: "Group",
|
||||||
Categories: []string{"k9s"},
|
Categories: []string{"k9s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPreferred(f Factory, m ResourceMetas) error {
|
func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
|
|
@ -221,8 +221,8 @@ func extractMeta(o runtime.Object) (metav1.APIResource, []error) {
|
||||||
|
|
||||||
var meta map[string]interface{}
|
var meta map[string]interface{}
|
||||||
meta, errs = extractMap(crd.Object, "metadata", errs)
|
meta, errs = extractMap(crd.Object, "metadata", errs)
|
||||||
|
|
||||||
m.Name, errs = extractStr(meta, "name", errs)
|
m.Name, errs = extractStr(meta, "name", errs)
|
||||||
|
|
||||||
m.Group, errs = extractStr(spec, "group", errs)
|
m.Group, errs = extractStr(spec, "group", errs)
|
||||||
m.Version, errs = extractStr(spec, "version", errs)
|
m.Version, errs = extractStr(spec, "version", errs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@ type ContextKey string
|
||||||
// A collection of context keys.
|
// A collection of context keys.
|
||||||
const (
|
const (
|
||||||
KeyFactory ContextKey = "factory"
|
KeyFactory ContextKey = "factory"
|
||||||
KeyLabels = "labels"
|
KeyLabels ContextKey = "labels"
|
||||||
KeyFields = "fields"
|
KeyFields ContextKey = "fields"
|
||||||
KeyTable = "table"
|
KeyTable ContextKey = "table"
|
||||||
KeyDir = "dir"
|
KeyDir ContextKey = "dir"
|
||||||
KeyPath = "path"
|
KeyPath ContextKey = "path"
|
||||||
KeySubject = "subject"
|
KeySubject ContextKey = "subject"
|
||||||
KeyGVR = "gvr"
|
KeyGVR ContextKey = "gvr"
|
||||||
KeyForwards = "forwards"
|
KeyForwards ContextKey = "forwards"
|
||||||
KeyContainers = "containers"
|
KeyContainers ContextKey = "containers"
|
||||||
KeyBenchCfg = "benchcfg"
|
KeyBenchCfg ContextKey = "benchcfg"
|
||||||
KeyAliases = "aliases"
|
KeyAliases ContextKey = "aliases"
|
||||||
KeyUID = "uid"
|
KeyUID ContextKey = "uid"
|
||||||
KeySubjectKind = "subjectKind"
|
KeySubjectKind ContextKey = "subjectKind"
|
||||||
KeySubjectName = "subjectName"
|
KeySubjectName ContextKey = "subjectName"
|
||||||
KeyNamespace = "namespace"
|
KeyNamespace ContextKey = "namespace"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,6 @@ func (c *Job) List(ctx context.Context) ([]runtime.Object, error) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// 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 {
|
func isNamedAfter(p, n string) bool {
|
||||||
tokens := strings.Split(n, "-")
|
tokens := strings.Split(n, "-")
|
||||||
if len(tokens) == 0 || tokens[0] != p {
|
if len(tokens) == 0 || tokens[0] != p {
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ func (n *Node) List(_ context.Context) ([]runtime.Object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
oo := make([]runtime.Object, len(nn.Items))
|
oo := make([]runtime.Object, len(nn.Items))
|
||||||
for i, n := range nn.Items {
|
for i := range nn.Items {
|
||||||
o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&n)
|
o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&nn.Items[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,25 @@ func (s *Subject) List(ctx context.Context) ([]runtime.Object, error) {
|
||||||
return nil, errors.New("expecting a SubjectKind")
|
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)
|
crbs, err := fetchClusterRoleBindings(s.factory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
oo := make([]runtime.Object, 0, len(crbs))
|
oo := make([]runtime.Object, 0, len(crbs))
|
||||||
for _, crb := range crbs {
|
for _, crb := range crbs {
|
||||||
for _, su := range crb.Subjects {
|
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)
|
rbs, err := fetchRoleBindings(s.factory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oo := make([]runtime.Object, 0, len(rbs))
|
||||||
for _, rb := range rbs {
|
for _, rb := range rbs {
|
||||||
for _, su := range rb.Subjects {
|
for _, su := range rb.Subjects {
|
||||||
if su.Kind != kind || inSubjectRes(oo, su.Name) {
|
if su.Kind != kind || inSubjectRes(oo, su.Name) {
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ func NewTable(gvr string) *Table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start initiates model updates.
|
// Watch initiates model updates.
|
||||||
func (t *Table) Start(ctx context.Context) {
|
func (t *Table) Watch(ctx context.Context) {
|
||||||
t.Refresh(ctx)
|
t.Refresh(ctx)
|
||||||
go t.updater(ctx)
|
go t.updater(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,6 @@ func (v *CmdView) write(s string) {
|
||||||
fmt.Fprintf(v, defaultPrompt, v.icon, s)
|
fmt.Fprintf(v, defaultPrompt, v.icon, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *CmdView) reset() {
|
|
||||||
v.update("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Event Listener protocol...
|
// Event Listener protocol...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,39 @@ import (
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tabular interface {
|
// Namespaceable represents a namespaceable model.
|
||||||
Empty() bool
|
type Namespaceable interface {
|
||||||
Peek() render.TableData
|
// ClusterWide returns true if the model represents resource in all namespaces.
|
||||||
ClusterWide() bool
|
ClusterWide() bool
|
||||||
|
|
||||||
|
// GetNamespace returns the model namespace.
|
||||||
GetNamespace() string
|
GetNamespace() string
|
||||||
|
|
||||||
|
// SetNamespace changes the model namespace.
|
||||||
SetNamespace(string)
|
SetNamespace(string)
|
||||||
AddListener(model.TableListener)
|
|
||||||
Start(context.Context)
|
// InNamespace check if current namespace matches models.
|
||||||
InNamespace(string) bool
|
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)
|
SetRefreshRate(time.Duration)
|
||||||
|
|
||||||
|
// AddListener registers a model listener.
|
||||||
|
AddListener(model.TableListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selectable represents a table with selections.
|
// Selectable represents a table with selections.
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ func (t *testModel) ClusterWide() bool { return false }
|
||||||
func (t *testModel) GetNamespace() string { return "blee" }
|
func (t *testModel) GetNamespace() string { return "blee" }
|
||||||
func (t *testModel) SetNamespace(string) {}
|
func (t *testModel) SetNamespace(string) {}
|
||||||
func (t *testModel) AddListener(model.TableListener) {}
|
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) InNamespace(string) bool { return true }
|
||||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ func (t *testModel) ClusterWide() bool { return false }
|
||||||
func (t *testModel) GetNamespace() string { return "blee" }
|
func (t *testModel) GetNamespace() string { return "blee" }
|
||||||
func (t *testModel) SetNamespace(string) {}
|
func (t *testModel) SetNamespace(string) {}
|
||||||
func (t *testModel) AddListener(model.TableListener) {}
|
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) InNamespace(string) bool { return true }
|
||||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !dao.IsK9sMeta(b.meta) {
|
if !dao.IsK9sMeta(b.meta) {
|
||||||
if _, err := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR()); err != nil {
|
if _, e := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR()); e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,8 +105,7 @@ func (b *Browser) Start() {
|
||||||
if path, ok := ctx.Value(internal.KeyPath).(string); ok && path != "" {
|
if path, ok := ctx.Value(internal.KeyPath).(string); ok && path != "" {
|
||||||
b.Path = path
|
b.Path = path
|
||||||
}
|
}
|
||||||
|
b.GetModel().Watch(ctx)
|
||||||
b.GetModel().Start(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates browser updates.
|
// Stop terminates browser updates.
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func (t *testTableModel) ClusterWide() bool { return false }
|
||||||
func (t *testTableModel) GetNamespace() string { return "blee" }
|
func (t *testTableModel) GetNamespace() string { return "blee" }
|
||||||
func (t *testTableModel) SetNamespace(string) {}
|
func (t *testTableModel) SetNamespace(string) {}
|
||||||
func (t *testTableModel) AddListener(model.TableListener) {}
|
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) InNamespace(string) bool { return true }
|
||||||
func (t *testTableModel) SetRefreshRate(time.Duration) {}
|
func (t *testTableModel) SetRefreshRate(time.Duration) {}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue