327 lines
7.9 KiB
Go
327 lines
7.9 KiB
Go
package view
|
|
|
|
import (
|
|
"context"
|
|
|
|
"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/gdamore/tcell"
|
|
"github.com/rs/zerolog/log"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
)
|
|
|
|
const (
|
|
ClusterRole roleKind = iota
|
|
Role
|
|
|
|
clusterWide = "*"
|
|
rbacTitle = "Policies"
|
|
rbacTitleFmt = " [fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-][[count:bg:b]%d[fg:bg:-]][fg:bg:-] "
|
|
)
|
|
|
|
var (
|
|
k8sVerbs = []string{
|
|
"get",
|
|
"list",
|
|
"watch",
|
|
"create",
|
|
"patch",
|
|
"update",
|
|
"delete",
|
|
"deletecollection",
|
|
}
|
|
|
|
httpTok8sVerbs = map[string]string{
|
|
"post": "create",
|
|
"put": "update",
|
|
}
|
|
)
|
|
|
|
type roleKind = int8
|
|
|
|
// Rbac presents an RBAC policy viewer.
|
|
type Rbac struct {
|
|
ResourceViewer
|
|
}
|
|
|
|
// NewRbac returns a new viewer.
|
|
func NewRbac(gvr client.GVR) ResourceViewer {
|
|
log.Debug().Msgf(">>>>> NEWRBAC %v!!!!!", gvr)
|
|
r := Rbac{
|
|
ResourceViewer: NewBrowser(gvr),
|
|
}
|
|
r.GetTable().SetColorerFn(render.Rbac{}.ColorerFunc())
|
|
r.SetBindKeysFn(r.bindKeys)
|
|
r.GetTable().SetSortCol(1, len(render.Rbac{}.Header(render.ClusterScope)), true)
|
|
|
|
return &r
|
|
}
|
|
|
|
func (r *Rbac) showPolicies(app *App, ns, resource, selection string) {
|
|
log.Debug().Msgf("SHOWING!! %q--%q--%q", ns, resource, selection)
|
|
}
|
|
|
|
func (r *Rbac) UpdateTitle() {
|
|
// BOZO!!
|
|
// r.GetTable().SetTitle(ui.SkinTitle(fmt.Sprintf(rbacTitleFmt, rbacTitle, r.path, r.GetRowCount()-1), r.app.Styles.Frame()))
|
|
}
|
|
|
|
func (r *Rbac) bindKeys(aa ui.KeyActions) {
|
|
aa.Delete(ui.KeyShiftA, tcell.KeyCtrlSpace, ui.KeySpace)
|
|
aa.Add(ui.KeyActions{
|
|
// BOZO!!
|
|
// tcell.KeyEscape: ui.NewKeyAction("Reset", r.resetCmd, false),
|
|
// ui.KeySlash: ui.NewKeyAction("Filter", r.activateCmd, false),
|
|
ui.KeyShiftO: ui.NewKeyAction("Sort APIGroup", r.GetTable().SortColCmd(1, true), false),
|
|
})
|
|
}
|
|
|
|
// BOZO!!
|
|
// func (r *Rbac) refresh() {
|
|
// if r.app.Conn() == nil {
|
|
// return
|
|
// }
|
|
// data, err := r.reconcile(r.roleName, r.roleType)
|
|
// if err != nil {
|
|
// log.Error().Err(err).Msgf("Refresh for %s:%d", r.roleName, r.roleType)
|
|
// r.app.Flash().Err(err)
|
|
// }
|
|
// r.Update(data)
|
|
// r.UpdateTitle()
|
|
// }
|
|
|
|
// func (r *Rbac) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
// if !r.GetTable().SearchBuff().Empty() {
|
|
// r.GetTable().SearchBuff().Reset()
|
|
// return nil
|
|
// }
|
|
|
|
// return r.App().PrevCmd(evt)
|
|
// }
|
|
|
|
// func (r *Rbac) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
// if r.cancelFn != nil {
|
|
// r.cancelFn()
|
|
// }
|
|
|
|
// if r.SearchBuff().IsActive() {
|
|
// r.SearchBuff().Reset()
|
|
// return nil
|
|
// }
|
|
|
|
// return r.app.PrevCmd(evt)
|
|
// }
|
|
|
|
// func (r *Rbac) reconcile(name string, kind roleKind) (render.TableData, error) {
|
|
// var table render.TableData
|
|
|
|
// rows, err := r.fetchRoles(name, kind)
|
|
// if err != nil {
|
|
// return table, err
|
|
// }
|
|
|
|
// return buildTable(r, rows), nil
|
|
// }
|
|
|
|
// func (r *Rbac) Header() render.HeaderRow {
|
|
// return render.Rbac{}.Header(render.AllNamespaces)
|
|
// }
|
|
|
|
// func (r *Rbac) GetCache() render.RowEvents {
|
|
// return r.cache
|
|
// }
|
|
|
|
// func (r *Rbac) SetCache(evts render.RowEvents) {
|
|
// r.cache = evts
|
|
// }
|
|
|
|
// func (r *Rbac) fetchRoles(name string, kind roleKind) (render.Rows, error) {
|
|
// switch kind {
|
|
// case ClusterRole:
|
|
// return r.loadClusterRoles(name)
|
|
// case Role:
|
|
// return r.loadRoles(name)
|
|
// default:
|
|
// return nil, fmt.Errorf("Expecting clusterrole/role but found %d", kind)
|
|
// }
|
|
// }
|
|
|
|
// func (r *Rbac) loadClusterRoles(name string) (render.Rows, error) {
|
|
// o, err := r.app.factory.Get("-", "rbac.authorization.k8s.io/v1/clusterroles", name, labels.Everything())
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// var cr rbacv1.ClusterRole
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// return r.parseRules(cr.Rules), nil
|
|
// }
|
|
|
|
// func (r *Rbac) loadRoles(path string) (render.Rows, error) {
|
|
// ns, n := client.Namespaced(path)
|
|
// o, err := r.app.factory.Get(ns, "rbac.authorization.k8s.io/v1/roles", n, labels.Everything())
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// var ro rbacv1.Role
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ro)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// return r.parseRules(ro.Rules), nil
|
|
// }
|
|
|
|
// func (r *Rbac) parseRules(rules []rbacv1.PolicyRule) render.Rows {
|
|
// m := make(render.Rows, 0, len(rules))
|
|
// for _, rule := range rules {
|
|
// for _, grp := range rule.APIGroups {
|
|
// for _, res := range rule.Resources {
|
|
// k := res
|
|
// if grp != "" {
|
|
// k = res + "." + grp
|
|
// }
|
|
// for _, na := range rule.ResourceNames {
|
|
// m = m.Upsert(r.prepRow(fqn(k, na), grp, rule.Verbs))
|
|
// }
|
|
// m = m.Upsert(r.prepRow(k, grp, rule.Verbs))
|
|
// }
|
|
// }
|
|
// for _, nres := range rule.NonResourceURLs {
|
|
// if nres[0] != '/' {
|
|
// nres = "/" + nres
|
|
// }
|
|
// m = m.Upsert(r.prepRow(nres, "", rule.Verbs))
|
|
// }
|
|
// }
|
|
|
|
// return m
|
|
// }
|
|
|
|
// func (r *Rbac) prepRow(res, grp string, verbs []string) render.Row {
|
|
// if grp != "" {
|
|
// grp = toGroup(grp)
|
|
// }
|
|
|
|
// fields := make(render.Fields, 0, len(r.Header()))
|
|
// fields = append(fields, res, group)
|
|
// return render.Row{
|
|
// ID: res,
|
|
// Fields: append(fields, verbs...),
|
|
// }
|
|
// }
|
|
|
|
// func asVerbs(verbs ...string) []string {
|
|
// const (
|
|
// verbLen = 4
|
|
// unknownLen = 30
|
|
// )
|
|
|
|
// r := make([]string, 0, len(k8sVerbs)+1)
|
|
// for _, v := range k8sVerbs {
|
|
// r = append(r, toVerbIcon(hasVerb(verbs, v)))
|
|
// }
|
|
|
|
// var unknowns []string
|
|
// for _, v := range verbs {
|
|
// if hv, ok := httpTok8sVerbs[v]; ok {
|
|
// v = hv
|
|
// }
|
|
// if !hasVerb(k8sVerbs, v) && v != clusterWide {
|
|
// unknowns = append(unknowns, v)
|
|
// }
|
|
// }
|
|
|
|
// return append(r, resource.Truncate(strings.Join(unknowns, ","), unknownLen))
|
|
// }
|
|
|
|
// func toVerbIcon(ok bool) string {
|
|
// if ok {
|
|
// return "[green::b] ✓ [::]"
|
|
// }
|
|
// return "[orangered::b] 𐄂 [::]"
|
|
// }
|
|
|
|
// func hasVerb(verbs []string, verb string) bool {
|
|
// if len(verbs) == 1 && verbs[0] == clusterWide {
|
|
// return true
|
|
// }
|
|
|
|
// for _, v := range verbs {
|
|
// if hv, ok := httpTok8sVerbs[v]; ok {
|
|
// if hv == verb {
|
|
// return true
|
|
// }
|
|
// }
|
|
// if v == verb {
|
|
// return true
|
|
// }
|
|
// }
|
|
|
|
// return false
|
|
// }
|
|
|
|
// func toGroup(g string) string {
|
|
// if g == "" {
|
|
// return "v1"
|
|
// }
|
|
// return g
|
|
// }
|
|
|
|
func showRoleBinding(app *App, _, resource, selection string) {
|
|
// ns, n := client.Namespaced(selection)
|
|
// rb, err := app.Conn().DialOrDie().RbacV1().RoleBindings(ns).Get(n, metav1.GetOptions{})
|
|
// if err != nil {
|
|
// app.Flash().Errf("Unable to retrieve rolebindings for %s", selection)
|
|
// return
|
|
// }
|
|
// BOZO!!
|
|
// app.inject(NewRbac(fqn(ns, rb.RoleRef.Name), Role, selection))
|
|
}
|
|
|
|
func showClusterRoleBinding(app *App, ns, gvr, path string) {
|
|
o, err := app.factory.Get("rbac.authorization.k8s.io/v1/clusterrolebindings", path, labels.Everything())
|
|
if err != nil {
|
|
app.Flash().Err(err)
|
|
return
|
|
}
|
|
|
|
var crb rbacv1.ClusterRoleBinding
|
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
|
if err != nil {
|
|
app.Flash().Errf("Unable to retrieve clusterrolebindings for %s", path)
|
|
return
|
|
}
|
|
|
|
// BOZO!! Must make sure cluster roles are in cache prior to loading rbac view.
|
|
app.factory.ForResource("-", "rbac.authorization.k8s.io/v1/clusterroles")
|
|
app.factory.WaitForCacheSync()
|
|
|
|
// BOZO!!
|
|
// app.inject(NewRbac(crb.RoleRef.Name, ClusterRole, selection))
|
|
}
|
|
|
|
func showRBAC(app *App, _, gvr, path string) {
|
|
log.Debug().Msgf("Showing RBAC %q--%q", gvr, path)
|
|
v := NewRbac(client.GVR("rbac"))
|
|
v.SetContextFn(rbacCtxt(app, gvr, path))
|
|
app.inject(v)
|
|
}
|
|
|
|
func rbacCtxt(app *App, gvr, path string) ContextFunc {
|
|
return func(ctx context.Context) context.Context {
|
|
ctx = context.WithValue(ctx, internal.KeyPath, path)
|
|
return context.WithValue(ctx, internal.KeyGVR, gvr)
|
|
}
|
|
}
|