k9s/internal/views/registrar.go

494 lines
9.7 KiB
Go

package views
import (
"strings"
"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(ns string, app *appView, list resource.List) resourceViewer
listFn func(c resource.Connection, ns string) resource.List
enterFn func(app *appView, ns, resource, selection string)
decorateFn func(resource.TableData) resource.TableData
crdCmd struct {
api string
version string
plural string
singular string
}
resCmd struct {
crdCmd
title string
viewFn viewFn
listFn listFn
enterFn enterFn
colorerFn ui.ColorerFunc
decorateFn decorateFn
}
)
func aliasCmds(c k8s.Connection, m map[string]resCmd) {
resourceViews(c, m)
if c != nil {
allCRDs(c, m)
}
}
func allCRDs(c k8s.Connection, m map[string]resCmd) {
crds, _ := resource.NewCustomResourceDefinitionList(c, resource.AllNamespaces).
Resource().
List(resource.AllNamespaces)
for _, crd := range crds {
ff := crd.ExtFields()
grp := k8s.APIGroup{
GKV: k8s.GKV{
Group: ff["group"].(string),
Kind: ff["kind"].(string),
Version: ff["version"].(string),
},
}
res := resCmd{
title: grp.Kind,
crdCmd: crdCmd{
api: grp.Group,
version: grp.Version,
},
}
if p, ok := ff["plural"].(string); ok {
res.plural = p
m[p] = res
}
if s, ok := ff["singular"].(string); ok {
res.singular = s
m[s] = res
}
if aa, ok := ff["aliases"].([]interface{}); ok {
for _, a := range aa {
m[a.(string)] = res
}
}
}
}
func showRBAC(app *appView, ns, resource, selection string) {
kind := clusterRole
if resource == "role" {
kind = role
}
app.inject(newRBACView(app, ns, selection, kind))
}
func showCRD(app *appView, 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 *appView, 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(newRBACView(app, ns, crb.RoleRef.Name, clusterRole))
}
func showRole(app *appView, _, 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(newRBACView(app, ns, fqn(ns, rb.RoleRef.Name), role))
}
func showSAPolicy(app *appView, _, _, selection string) {
_, n := namespaced(selection)
app.inject(newPolicyView(app, mapFuSubject("ServiceAccount"), n))
}
func resourceViews(c k8s.Connection, m map[string]resCmd) {
primRes(m)
coreRes(m)
stateRes(m)
rbacRes(m)
apiExtRes(m)
batchRes(m)
appsRes(m)
extRes(m)
v1beta1Res(m)
custRes(m)
if c != nil {
hpaRes(c, m)
}
}
func stateRes(m map[string]resCmd) {
m["cm"] = resCmd{
title: "ConfigMaps",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewConfigMapList,
}
m["pv"] = resCmd{
title: "PersistentVolumes",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewPersistentVolumeList,
colorerFn: pvColorer,
}
m["pvc"] = resCmd{
title: "PersistentVolumeClaims",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewPersistentVolumeClaimList,
colorerFn: pvcColorer,
}
m["sec"] = resCmd{
title: "Secrets",
crdCmd: crdCmd{
api: "",
},
viewFn: newSecretView,
listFn: resource.NewSecretList,
}
m["sc"] = resCmd{
title: "StorageClasses",
crdCmd: crdCmd{
api: "storage.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewStorageClassList,
}
}
func primRes(m map[string]resCmd) {
m["no"] = resCmd{
title: "Nodes",
crdCmd: crdCmd{
api: "",
},
viewFn: newNodeView,
listFn: resource.NewNodeList,
colorerFn: nsColorer,
}
m["ns"] = resCmd{
title: "Namespaces",
crdCmd: crdCmd{
api: "",
},
viewFn: newNamespaceView,
listFn: resource.NewNamespaceList,
colorerFn: nsColorer,
}
m["po"] = resCmd{
title: "Pods",
crdCmd: crdCmd{
api: "",
},
viewFn: newPodView,
listFn: resource.NewPodList,
colorerFn: podColorer,
}
m["sa"] = resCmd{
title: "ServiceAccounts",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewServiceAccountList,
enterFn: showSAPolicy,
}
m["svc"] = resCmd{
title: "Services",
crdCmd: crdCmd{
api: "",
},
viewFn: newSvcView,
listFn: resource.NewServiceList,
}
}
func coreRes(m map[string]resCmd) {
m["ctx"] = resCmd{
title: "Contexts",
crdCmd: crdCmd{
api: "",
},
viewFn: newContextView,
listFn: resource.NewContextList,
colorerFn: ctxColorer,
}
m["ds"] = resCmd{
title: "DaemonSets",
crdCmd: crdCmd{
api: "",
},
viewFn: newDaemonSetView,
listFn: resource.NewDaemonSetList,
colorerFn: dpColorer,
}
m["ep"] = resCmd{
title: "EndPoints",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewEndpointsList,
}
m["ev"] = resCmd{
title: "Events",
crdCmd: crdCmd{
api: "",
},
viewFn: newResourceView,
listFn: resource.NewEventList,
colorerFn: evColorer,
}
m["rc"] = resCmd{
title: "ReplicationControllers",
crdCmd: crdCmd{
api: "",
},
viewFn: newScalableResourceView,
listFn: resource.NewReplicationControllerList,
colorerFn: rsColorer,
}
}
func custRes(m map[string]resCmd) {
m["usr"] = resCmd{
title: "Users",
crdCmd: crdCmd{
api: "",
},
viewFn: newSubjectView,
}
m["grp"] = resCmd{
title: "Groups",
crdCmd: crdCmd{
api: "",
},
viewFn: newSubjectView,
}
m["pf"] = resCmd{
title: "PortForward",
crdCmd: crdCmd{
api: "",
},
viewFn: newForwardView,
}
m["be"] = resCmd{
title: "Benchmark",
crdCmd: crdCmd{
api: "",
},
viewFn: newBenchView,
}
m["sd"] = resCmd{
title: "ScreenDumps",
crdCmd: crdCmd{
api: "",
},
viewFn: newDumpView,
}
}
func rbacRes(m map[string]resCmd) {
m["cr"] = resCmd{
title: "ClusterRoles",
crdCmd: crdCmd{
api: "rbac.authorization.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewClusterRoleList,
enterFn: showRBAC,
}
m["crb"] = resCmd{
title: "ClusterRoleBindings",
crdCmd: crdCmd{
api: "rbac.authorization.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewClusterRoleBindingList,
enterFn: showClusterRole,
}
m["rb"] = resCmd{
title: "RoleBindings",
crdCmd: crdCmd{
api: "rbac.authorization.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewRoleBindingList,
enterFn: showRole,
}
m["ro"] = resCmd{
title: "Roles",
crdCmd: crdCmd{
api: "rbac.authorization.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewRoleList,
enterFn: showRBAC,
}
}
func apiExtRes(m map[string]resCmd) {
m["crd"] = resCmd{
title: "CustomResourceDefinitions",
crdCmd: crdCmd{
api: "apiextensions.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewCustomResourceDefinitionList,
enterFn: showCRD,
}
m["np"] = resCmd{
title: "NetworkPolicies",
crdCmd: crdCmd{
api: "apiextensions.k8s.io",
},
viewFn: newResourceView,
listFn: resource.NewNetworkPolicyList,
}
}
func batchRes(m map[string]resCmd) {
m["cj"] = resCmd{
title: "CronJobs",
crdCmd: crdCmd{
api: "batch",
},
viewFn: newCronJobView,
listFn: resource.NewCronJobList,
}
m["jo"] = resCmd{
title: "Jobs",
crdCmd: crdCmd{
api: "batch",
},
viewFn: newJobView,
listFn: resource.NewJobList,
}
}
func appsRes(m map[string]resCmd) {
m["dp"] = resCmd{
title: "Deployments",
crdCmd: crdCmd{
api: "apps",
},
viewFn: newDeployView,
listFn: resource.NewDeploymentList,
colorerFn: dpColorer,
}
m["rs"] = resCmd{
title: "ReplicaSets",
crdCmd: crdCmd{
api: "apps",
},
viewFn: newReplicaSetView,
listFn: resource.NewReplicaSetList,
colorerFn: rsColorer,
}
m["sts"] = resCmd{
title: "StatefulSets",
crdCmd: crdCmd{
api: "apps",
},
viewFn: newStatefulSetView,
listFn: resource.NewStatefulSetList,
colorerFn: stsColorer,
}
}
func extRes(m map[string]resCmd) {
m["ing"] = resCmd{
title: "Ingress",
crdCmd: crdCmd{
api: "extensions",
},
viewFn: newResourceView,
listFn: resource.NewIngressList,
}
}
func v1beta1Res(m map[string]resCmd) {
m["pdb"] = resCmd{
title: "PodDisruptionBudgets",
crdCmd: crdCmd{
api: "v1.beta1",
},
viewFn: newResourceView,
listFn: resource.NewPDBList,
colorerFn: pdbColorer,
}
}
func hpaRes(c k8s.Connection, cmds map[string]resCmd) {
rev, ok, err := c.SupportsRes("autoscaling", []string{"v1", "v2beta1", "v2beta2"})
if err != nil {
log.Error().Err(err).Msg("Checking HPA")
return
}
if !ok {
log.Error().Msg("HPA are not supported on this cluster")
return
}
switch rev {
case "v1":
cmds["hpa"] = resCmd{
title: "HorizontalPodAutoscalers",
crdCmd: crdCmd{
api: "autoscaling",
},
viewFn: newResourceView,
listFn: resource.NewHorizontalPodAutoscalerV1List,
}
case "v2beta1":
cmds["hpa"] = resCmd{
title: "HorizontalPodAutoscalers",
crdCmd: crdCmd{
api: "autoscaling",
},
viewFn: newResourceView,
listFn: resource.NewHorizontalPodAutoscalerV2Beta1List,
}
case "v2beta2":
cmds["hpa"] = resCmd{
title: "HorizontalPodAutoscalers",
crdCmd: crdCmd{
api: "autoscaling",
},
viewFn: newResourceView,
listFn: resource.NewHorizontalPodAutoscalerList,
}
default:
log.Panic().Msgf("K9s unsupported HPA version. Exiting!")
}
}