k9s/internal/view/registrar.go

341 lines
8.1 KiB
Go

package view
import (
"strings"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/k8s"
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/k9s/internal/ui"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type (
viewFn func(title, gvr string, list resource.List) ResourceViewer
listFn func(c resource.Connection, ns string) resource.List
enterFn func(app *App, ns, resource, selection string)
decorateFn func(resource.TableData) resource.TableData
viewer struct {
gvr string
kind string
namespaced bool
verbs metav1.Verbs
viewFn viewFn
listFn listFn
enterFn enterFn
colorerFn ui.ColorerFunc
decorateFn decorateFn
}
viewers map[string]viewer
)
func listFunc(l resource.List) viewFn {
return func(title, gvr string, list resource.List) ResourceViewer {
return NewResource(title, gvr, l)
}
}
var aliases = config.NewAliases()
func allCRDs(c k8s.Connection, vv viewers) {
crds, err := resource.NewCustomResourceDefinitionList(c, resource.AllNamespaces).
Resource().
List(resource.AllNamespaces, metav1.ListOptions{})
if err != nil {
log.Error().Err(err).Msg("CRDs load fail")
return
}
for _, crd := range crds {
meta, err := crd.ExtFields()
if err != nil {
log.Error().Err(err).Msgf("Error getting extended fields from %s", crd.Name())
continue
}
gvr := k8s.NewGVR(meta.Group, meta.Version, meta.Plural)
gvrs := gvr.String()
if meta.Plural != "" {
aliases.Define(gvrs, meta.Plural)
}
if meta.Singular != "" {
aliases.Define(gvrs, meta.Singular)
}
for _, a := range meta.ShortNames {
aliases.Define(gvrs, a)
}
vv[gvrs] = viewer{
gvr: gvrs,
kind: meta.Kind,
viewFn: listFunc(resource.NewCustomList(c, meta.Namespaced, "", gvrs)),
colorerFn: ui.DefaultColorer,
}
}
}
func showRBAC(app *App, ns, resource, selection string) {
kind := clusterRole
if resource == "role" {
kind = role
}
app.inject(NewRBAC(app, ns, selection, kind))
}
func showCRD(app *App, ns, resource, selection string) {
log.Debug().Msgf("Launching CRD %q -- %q -- %q", ns, resource, selection)
tokens := strings.Split(selection, ".")
app.gotoResource(tokens[0], true)
}
func showClusterRole(app *App, ns, resource, selection string) {
crb, err := app.Conn().DialOrDie().RbacV1().ClusterRoleBindings().Get(selection, metav1.GetOptions{})
if err != nil {
app.Flash().Errf("Unable to retrieve clusterrolebindings for %s", selection)
return
}
app.inject(NewRBAC(app, ns, crb.RoleRef.Name, clusterRole))
}
func showRole(app *App, _, resource, selection string) {
ns, n := 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
}
app.inject(NewRBAC(app, ns, fqn(ns, rb.RoleRef.Name), role))
}
func showSAPolicy(app *App, _, _, selection string) {
_, n := namespaced(selection)
app.inject(NewPolicy(app, mapFuSubject("ServiceAccount"), n))
}
func load(c k8s.Connection, vv viewers) {
if err := aliases.Load(); err != nil {
log.Error().Err(err).Msg("No custom aliases defined in config")
}
discovery, err := c.CachedDiscovery()
if err != nil {
log.Error().Err(err).Msgf("Error to get discovery client")
return
}
rr, _ := discovery.ServerPreferredResources()
for _, r := range rr {
for _, res := range r.APIResources {
gvr := k8s.ToGVR(r.GroupVersion, res.Name)
cmd, ok := vv[gvr.String()]
if !ok {
// log.Debug().Msgf(fmt.Sprintf(">> No viewer defined for `%s`", gvr))
continue
}
cmd.namespaced = res.Namespaced
cmd.kind = res.Kind
cmd.verbs = res.Verbs
cmd.gvr = gvr.String()
vv[gvr.String()] = cmd
gvrStr := gvr.String()
aliases.Define(gvrStr, strings.ToLower(res.Kind))
aliases.Define(gvrStr, res.Name)
if len(res.SingularName) > 0 {
aliases.Define(gvrStr, res.SingularName)
}
for _, s := range res.ShortNames {
aliases.Define(gvrStr, s)
}
}
}
}
func resourceViews(c k8s.Connection, m viewers) {
coreRes(m)
miscRes(m)
appsRes(m)
authRes(m)
extRes(m)
netRes(m)
batchRes(m)
policyRes(m)
hpaRes(m)
load(c, m)
}
func coreRes(vv viewers) {
vv["v1/nodes"] = viewer{
viewFn: NewNode,
listFn: resource.NewNodeList,
colorerFn: nsColorer,
}
vv["v1/namespaces"] = viewer{
viewFn: NewNamespace,
listFn: resource.NewNamespaceList,
colorerFn: nsColorer,
}
vv["v1/pods"] = viewer{
viewFn: NewPod,
listFn: resource.NewPodList,
colorerFn: podColorer,
}
vv["v1/serviceaccounts"] = viewer{
listFn: resource.NewServiceAccountList,
enterFn: showSAPolicy,
}
vv["v1/services"] = viewer{
viewFn: NewService,
listFn: resource.NewServiceList,
}
vv["v1/configmaps"] = viewer{
listFn: resource.NewConfigMapList,
}
vv["v1/persistentvolumes"] = viewer{
listFn: resource.NewPersistentVolumeList,
colorerFn: pvColorer,
}
vv["v1/persistentvolumeclaims"] = viewer{
listFn: resource.NewPersistentVolumeClaimList,
colorerFn: pvcColorer,
}
vv["v1/secrets"] = viewer{
viewFn: NewSecret,
listFn: resource.NewSecretList,
}
vv["v1/endpoints"] = viewer{
listFn: resource.NewEndpointsList,
}
vv["v1/events"] = viewer{
listFn: resource.NewEventList,
colorerFn: evColorer,
}
vv["v1/replicationcontrollers"] = viewer{
viewFn: NewScalableResource,
listFn: resource.NewReplicationControllerList,
colorerFn: rsColorer,
}
}
func miscRes(vv viewers) {
vv["storage.k8s.io/v1/storageclasses"] = viewer{
listFn: resource.NewStorageClassList,
}
vv["contexts"] = viewer{
gvr: "contexts",
kind: "Contexts",
viewFn: NewContext,
listFn: resource.NewContextList,
colorerFn: ctxColorer,
}
vv["users"] = viewer{
gvr: "users",
viewFn: NewSubject,
}
vv["groups"] = viewer{
gvr: "groups",
viewFn: NewSubject,
}
vv["portforwards"] = viewer{
gvr: "portforwards",
viewFn: NewPortForward,
}
vv["benchmarks"] = viewer{
gvr: "benchmarks",
viewFn: NewBench,
}
vv["screendumps"] = viewer{
gvr: "screendumps",
viewFn: NewScreenDump,
}
}
func appsRes(vv viewers) {
vv["apps/v1/deployments"] = viewer{
viewFn: NewDeploy,
listFn: resource.NewDeploymentList,
colorerFn: dpColorer,
}
vv["apps/v1/replicasets"] = viewer{
viewFn: NewReplicaSet,
listFn: resource.NewReplicaSetList,
colorerFn: rsColorer,
}
vv["apps/v1/statefulsets"] = viewer{
viewFn: NewStatefulSet,
listFn: resource.NewStatefulSetList,
colorerFn: stsColorer,
}
vv["apps/v1/daemonsets"] = viewer{
viewFn: NewDaemonSet,
listFn: resource.NewDaemonSetList,
colorerFn: dpColorer,
}
}
func authRes(vv viewers) {
vv["rbac.authorization.k8s.io/v1/clusterroles"] = viewer{
listFn: resource.NewClusterRoleList,
enterFn: showRBAC,
}
vv["rbac.authorization.k8s.io/v1/clusterrolebindings"] = viewer{
listFn: resource.NewClusterRoleBindingList,
enterFn: showClusterRole,
}
vv["rbac.authorization.k8s.io/v1/rolebindings"] = viewer{
listFn: resource.NewRoleBindingList,
enterFn: showRole,
}
vv["rbac.authorization.k8s.io/v1/roles"] = viewer{
listFn: resource.NewRoleList,
enterFn: showRBAC,
}
}
func extRes(vv viewers) {
vv["apiextensions.k8s.io/v1/customresourcedefinitions"] = viewer{
listFn: resource.NewCustomResourceDefinitionList,
enterFn: showCRD,
}
vv["apiextensions.k8s.io/v1beta1/customresourcedefinitions"] = viewer{
listFn: resource.NewCustomResourceDefinitionList,
enterFn: showCRD,
}
}
func netRes(vv viewers) {
vv["networking.k8s.io/v1/networkpolicies"] = viewer{
listFn: resource.NewNetworkPolicyList,
}
vv["extensions/v1beta1/ingresses"] = viewer{
listFn: resource.NewIngressList,
}
}
func batchRes(vv viewers) {
vv["batch/v1beta1/cronjobs"] = viewer{
viewFn: NewCronJob,
listFn: resource.NewCronJobList,
}
vv["batch/v1/jobs"] = viewer{
viewFn: NewJob,
listFn: resource.NewJobList,
}
}
func policyRes(vv viewers) {
vv["policy/v1beta1/poddisruptionbudgets"] = viewer{
listFn: resource.NewPDBList,
colorerFn: pdbColorer,
}
}
func hpaRes(vv viewers) {
vv["autoscaling/v1/horizontalpodautoscalers"] = viewer{
listFn: resource.NewHorizontalPodAutoscalerV1List,
}
}