341 lines
8.1 KiB
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,
|
|
}
|
|
}
|