diff --git a/internal/k8s/mapper.go b/internal/k8s/mapper.go index 187e9149..9c8d9d9c 100644 --- a/internal/k8s/mapper.go +++ b/internal/k8s/mapper.go @@ -26,12 +26,12 @@ type RestMapper struct { Connection } -// Find a mapping given a resource name. -func (*RestMapper) Find(res string) (*meta.RESTMapping, error) { - if m, ok := resMap[res]; ok { +// Find a mapping given a resource kind. +func (*RestMapper) Find(kind string) (*meta.RESTMapping, error) { + if m, ok := kindToMapper[kind]; ok { return m, nil } - return nil, fmt.Errorf("no mapping for resource %s", res) + return nil, fmt.Errorf("no mapping for kind %s", kind) } // ToRESTMapper map resources to kind, and map kind and version to interfaces for manipulating K8s objects. @@ -116,18 +116,18 @@ func (*RestMapper) Name() meta.RESTScopeName { return meta.RESTScopeNameNamespace } -var resMap = map[string]*meta.RESTMapping{ - "ConfigMaps": { +var kindToMapper = map[string]*meta.RESTMapping{ + "ConfigMap": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmap"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"}, Scope: RestMapping, }, - "Pods": { + "Pod": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pod"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, Scope: RestMapping, }, - "Services": { + "Service": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "service"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"}, Scope: RestMapping, @@ -137,81 +137,81 @@ var resMap = map[string]*meta.RESTMapping{ GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Endpoints"}, Scope: RestMapping, }, - "Namespaces": { + "Namespace": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespace"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, Scope: RestMapping, }, - "Nodes": { + "Node": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "node"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Node"}, Scope: RestMapping, }, - "PersistentVolumes": { + "PersistentVolume": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolume"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "PersistentVolume"}, Scope: RestMapping, }, - "PersistentVolumeClaims": { + "PersistentVolumeClaim": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumeclaim"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}, Scope: RestMapping, }, - "ReplicationControllers": { + "ReplicationController": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "replicationcontroller"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ReplicationController"}, Scope: RestMapping, }, - "Secrets": { + "Secret": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secret"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"}, Scope: RestMapping, }, - "StorageClasses": { + "StorageClasse": { Resource: schema.GroupVersionResource{Group: "storage.k8s.io", Version: "v1", Resource: "storageclass"}, GroupVersionKind: schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}, Scope: RestMapping, }, - "ServiceAccounts": { + "ServiceAccount": { Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccount"}, GroupVersionKind: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ServiceAccount"}, Scope: RestMapping, }, - "Deployments": { + "Deployment": { Resource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployment"}, GroupVersionKind: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, Scope: RestMapping, }, - "ReplicaSets": { + "ReplicaSet": { Resource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicaset"}, GroupVersionKind: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"}, Scope: RestMapping, }, - "StatefulSets": { + "StatefulSet": { Resource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"}, GroupVersionKind: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}, Scope: RestMapping, }, - "HorizontalPodAutoscalers": { + "HorizontalPodAutoscaler": { Resource: schema.GroupVersionResource{Group: "autoscaling", Version: "v1", Resource: "horizontalpodautoscaler"}, GroupVersionKind: schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}, Scope: RestMapping, }, - "Jobs": { + "Job": { Resource: schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "job"}, GroupVersionKind: schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "Job"}, Scope: RestMapping, }, - "CronJobs": { + "CronJob": { Resource: schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "cronjob"}, GroupVersionKind: schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"}, Scope: RestMapping, }, - "DaemonSets": { + "DaemonSet": { Resource: schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "daemonset"}, GroupVersionKind: schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}, Scope: RestMapping, @@ -222,45 +222,45 @@ var resMap = map[string]*meta.RESTMapping{ Scope: RestMapping, }, - "ClusterRoles": { + "ClusterRole": { Resource: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrole"}, GroupVersionKind: schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, Scope: RestMapping, }, - "ClusterRoleBindings": { + "ClusterRoleBinding": { Resource: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebinding"}, GroupVersionKind: schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, Scope: RestMapping, }, - "Roles": { + "Role": { Resource: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "role"}, GroupVersionKind: schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"}, Scope: RestMapping, }, - "RoleBindings": { + "RoleBinding": { Resource: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebinding"}, GroupVersionKind: schema.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}, Scope: RestMapping, }, - "CustomResourceDefinitions": { + "CustomResourceDefinition": { Resource: schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1beta1", Resource: "customresourcedefinitions"}, GroupVersionKind: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: "CustomResourceDefinition"}, Scope: RestMapping, }, - "NetworkPolicies": { + "NetworkPolicy": { Resource: schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "networkpolicies"}, GroupVersionKind: schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}, Scope: RestMapping, }, - "Events": { + "Event": { Resource: schema.GroupVersionResource{Group: "events.k8s.io", Version: "v1beta1", Resource: "events"}, GroupVersionKind: schema.GroupVersionKind{Group: "events.k8s.io", Version: "v1beta1", Kind: "Event"}, Scope: RestMapping, }, - "PodDisruptionBudgets": { + "PodDisruptionBudget": { Resource: schema.GroupVersionResource{Group: "policy", Version: "v1beta1", Resource: "poddisruptionbudgets"}, GroupVersionKind: schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"}, Scope: RestMapping, diff --git a/internal/views/alias.go b/internal/views/alias.go index a8693fcc..f4e155e2 100644 --- a/internal/views/alias.go +++ b/internal/views/alias.go @@ -107,7 +107,7 @@ func (v *aliasView) runCmd(evt *tcell.EventKey) *tcell.EventKey { } func (v *aliasView) hydrate() resource.TableData { - cmds := make(map[string]resCmd, 40) + cmds := make(map[string]*resCmd, 40) aliasCmds(v.app.Conn(), cmds) data := resource.TableData{ @@ -119,7 +119,7 @@ func (v *aliasView) hydrate() resource.TableData { for k := range cmds { fields := resource.Row{ ui.Pad(k, 30), - ui.Pad(cmds[k].title, 30), + ui.Pad(cmds[k].kind, 30), ui.Pad(cmds[k].api, 30), } data.Rows[k] = &resource.RowEvent{ diff --git a/internal/views/alias_test.go b/internal/views/alias_test.go index 60471de4..0c4b1bf5 100644 --- a/internal/views/alias_test.go +++ b/internal/views/alias_test.go @@ -13,6 +13,6 @@ func TestAliasView(t *testing.T) { v.Init(nil, "") assert.Equal(t, 3, len(td.Header)) - assert.Equal(t, 33, len(td.Rows)) + assert.Equal(t, 41, len(td.Rows)) assert.Equal(t, "Aliases", v.getTitle()) } diff --git a/internal/views/command.go b/internal/views/command.go index 84e65a2a..32c04e3b 100644 --- a/internal/views/command.go +++ b/internal/views/command.go @@ -71,7 +71,7 @@ func (c *command) isStdCmd(cmd string) bool { } func (c *command) isAliasCmd(cmd string) bool { - cmds := make(map[string]resCmd, 30) + cmds := make(map[string]*resCmd, 30) resourceViews(c.app.Conn(), cmds) res, ok := cmds[cmd] if !ok { @@ -83,7 +83,7 @@ func (c *command) isAliasCmd(cmd string) bool { r = res.listFn(c.app.Conn(), resource.DefaultNamespace) } - v := res.viewFn(res.title, c.app, r) + v := res.viewFn(res.kind, c.app, r) if res.colorerFn != nil { v.setColorerFn(res.colorerFn) } @@ -94,8 +94,7 @@ func (c *command) isAliasCmd(cmd string) bool { v.setDecorateFn(res.decorateFn) } - const fmat = "Viewing resource %s..." - c.app.Flash().Infof(fmat, res.title) + c.app.Flash().Infof("Viewing resource %s...", res.kind) log.Debug().Msgf("Running command %s", cmd) c.exec(cmd, v) @@ -103,7 +102,7 @@ func (c *command) isAliasCmd(cmd string) bool { } func (c *command) isCRDCmd(cmd string) bool { - crds := map[string]resCmd{} + crds := map[string]*resCmd{} allCRDs(c.app.Conn(), crds) res, ok := crds[cmd] if !ok { @@ -115,7 +114,7 @@ func (c *command) isCRDCmd(cmd string) bool { name = res.singular } v := newResourceView( - res.title, + res.kind, c.app, resource.NewCustomList(c.app.Conn(), "", res.api, res.version, name), ) diff --git a/internal/views/registrar.go b/internal/views/registrar.go index 7cfaf9dd..ddcb904e 100644 --- a/internal/views/registrar.go +++ b/internal/views/registrar.go @@ -26,23 +26,49 @@ type ( resCmd struct { crdCmd - title string + kind string viewFn viewFn listFn listFn enterFn enterFn colorerFn ui.ColorerFunc decorateFn decorateFn } + + AliasConfig struct { + Aliases map[string]string `yaml:"aliases"` + } ) -func aliasCmds(c k8s.Connection, m map[string]resCmd) { +var DefaultAliasConfig = AliasConfig{ + Aliases: map[string]string{ + "Deployment": "dp", + "Secret": "sec", + "Jobs": "jo", + + "ClusterRoles": "cr", + "ClusterRoleBindings": "crb", + "RoleBindings": "rb", + "Roles": "ro", + + "NetworkPolicies": "np", + + "Contexts": "ctx", + "Users": "usr", + "Groups": "grp", + "PortForward": "pf", + "Benchmark": "be", + "ScreenDumps": "sd", + }, +} + +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) { +func allCRDs(c k8s.Connection, m map[string]*resCmd) { crds, _ := resource.NewCustomResourceDefinitionList(c, resource.AllNamespaces). Resource(). List(resource.AllNamespaces) @@ -59,7 +85,7 @@ func allCRDs(c k8s.Connection, m map[string]resCmd) { } res := resCmd{ - title: grp.Kind, + kind: grp.Kind, crdCmd: crdCmd{ api: grp.Group, version: grp.Version, @@ -67,17 +93,17 @@ func allCRDs(c k8s.Connection, m map[string]resCmd) { } if p, ok := ff["plural"].(string); ok { res.plural = p - m[p] = res + m[p] = &res } if s, ok := ff["singular"].(string); ok { res.singular = s - m[s] = res + m[s] = &res } if aa, ok := ff["aliases"].([]interface{}); ok { for _, a := range aa { - m[a.(string)] = res + m[a.(string)] = &res } } } @@ -122,372 +148,378 @@ func showSAPolicy(app *appView, _, _, selection string) { 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) +func collect(commandList ...[]*resCmd) (accum []*resCmd) { + for _, commands := range commandList { + accum = append(accum, commands...) + } + return +} + +func resourceViews(c k8s.Connection, cmdMap map[string]*resCmd) { + commands := collect( + primRes(), coreRes(), stateRes(), rbacRes(), apiExtRes(), + batchRes(), appsRes(), extRes(), v1beta1Res(), custRes(), + ) if c != nil { - hpaRes(c, m) + commands = append(commands, hpaRes(c)...) + } + + for _, rsc := range commands { + cmdMap[strings.ToLower(rsc.kind)] = rsc + } + + // Add default aliases + // TODO: read aliases from a config file. + for rsc, alias := range DefaultAliasConfig.Aliases { + if cmd, ok := cmdMap[strings.ToLower(rsc)]; ok { + addAlias(cmdMap, cmd, alias) + } + } + + if c != nil { + discoverAliasesFromServer(c, cmdMap) } } -func stateRes(m map[string]resCmd) { - m["cm"] = resCmd{ - title: "ConfigMaps", - crdCmd: crdCmd{ - api: "", - }, - viewFn: newResourceView, - listFn: resource.NewConfigMapList, +func addAlias(cmdMap map[string]*resCmd, cmd *resCmd, alias string) { + if alias == "" { + return } - 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, + + alias = strings.ToLower(alias) + if _, ok := cmdMap[alias]; !ok { + cmdMap[alias] = cmd } } -func primRes(m map[string]resCmd) { - m["no"] = resCmd{ - title: "Nodes", - crdCmd: crdCmd{ - api: "", - }, - viewFn: newNodeView, - listFn: resource.NewNodeList, - colorerFn: nsColorer, +func addAPIResourceAliases(cmds map[string]*resCmd, resource metav1.APIResource) { + if strings.Contains(resource.Name, "/") { + // Ignore resources that has slash, e.g., + // deployment/status, namespace/finalizers and etc. + return } - 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, + + if cmd, ok := cmds[strings.ToLower(resource.Kind)]; ok { + addAlias(cmds, cmd, resource.Name) + addAlias(cmds, cmd, resource.SingularName) + for _, sn := range resource.ShortNames { + addAlias(cmds, cmd, sn) + } } } -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 discoverAliasesFromServer(con k8s.Connection, cmds map[string]*resCmd) { + _, resourceLists, _ := con.DialOrDie().Discovery().ServerGroupsAndResources() + for _, resourceList := range resourceLists { + for _, resource := range resourceList.APIResources { + addAPIResourceAliases(cmds, resource) + } } } -func custRes(m map[string]resCmd) { - m["usr"] = resCmd{ - title: "Users", - crdCmd: crdCmd{ - api: "", +func stateRes() []*resCmd { + return []*resCmd{ + { + kind: "ConfigMap", + viewFn: newResourceView, + listFn: resource.NewConfigMapList, }, - viewFn: newSubjectView, - } - m["grp"] = resCmd{ - title: "Groups", - crdCmd: crdCmd{ - api: "", + { + kind: "PersistentVolume", + viewFn: newResourceView, + listFn: resource.NewPersistentVolumeList, + colorerFn: pvColorer, }, - viewFn: newSubjectView, - } - m["pf"] = resCmd{ - title: "PortForward", - crdCmd: crdCmd{ - api: "", + { + kind: "PersistentVolumeClaim", + viewFn: newResourceView, + listFn: resource.NewPersistentVolumeClaimList, + colorerFn: pvcColorer, }, - viewFn: newForwardView, - } - m["be"] = resCmd{ - title: "Benchmark", - crdCmd: crdCmd{ - api: "", + { + kind: "Secret", + viewFn: newSecretView, + listFn: resource.NewSecretList, }, - viewFn: newBenchView, - } - m["sd"] = resCmd{ - title: "ScreenDumps", - crdCmd: crdCmd{ - api: "", + { + kind: "StorageClass", + crdCmd: crdCmd{ + api: "storage.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewStorageClassList, }, - viewFn: newDumpView, } } -func rbacRes(m map[string]resCmd) { - m["cr"] = resCmd{ - title: "ClusterRoles", - crdCmd: crdCmd{ - api: "rbac.authorization.k8s.io", +func primRes() []*resCmd { + return []*resCmd{ + { + kind: "Node", + viewFn: newNodeView, + listFn: resource.NewNodeList, + colorerFn: nsColorer, }, - viewFn: newResourceView, - listFn: resource.NewClusterRoleList, - enterFn: showRBAC, - } - m["crb"] = resCmd{ - title: "ClusterRoleBindings", - crdCmd: crdCmd{ - api: "rbac.authorization.k8s.io", + { + kind: "Namespace", + viewFn: newNamespaceView, + listFn: resource.NewNamespaceList, + colorerFn: nsColorer, }, - viewFn: newResourceView, - listFn: resource.NewClusterRoleBindingList, - enterFn: showClusterRole, - } - - m["rb"] = resCmd{ - title: "RoleBindings", - crdCmd: crdCmd{ - api: "rbac.authorization.k8s.io", + { + kind: "Pod", + viewFn: newPodView, + listFn: resource.NewPodList, + colorerFn: podColorer, }, - viewFn: newResourceView, - listFn: resource.NewRoleBindingList, - enterFn: showRole, - } - m["ro"] = resCmd{ - title: "Roles", - crdCmd: crdCmd{ - api: "rbac.authorization.k8s.io", + { + kind: "ServiceAccount", + viewFn: newResourceView, + listFn: resource.NewServiceAccountList, + enterFn: showSAPolicy, + }, + { + kind: "Service", + viewFn: newSvcView, + listFn: resource.NewServiceList, }, - viewFn: newResourceView, - listFn: resource.NewRoleList, - enterFn: showRBAC, } } -func apiExtRes(m map[string]resCmd) { - m["crd"] = resCmd{ - title: "CustomResourceDefinitions", - crdCmd: crdCmd{ - api: "apiextensions.k8s.io", +func coreRes() []*resCmd { + return []*resCmd{ + { + kind: "Contexts", + viewFn: newContextView, + listFn: resource.NewContextList, + colorerFn: ctxColorer, }, - viewFn: newResourceView, - listFn: resource.NewCustomResourceDefinitionList, - enterFn: showCRD, - } - m["np"] = resCmd{ - title: "NetworkPolicies", - crdCmd: crdCmd{ - api: "apiextensions.k8s.io", + { + kind: "DaemonSet", + viewFn: newDaemonSetView, + listFn: resource.NewDaemonSetList, + colorerFn: dpColorer, + }, + { + kind: "EndPoints", + viewFn: newResourceView, + listFn: resource.NewEndpointsList, + }, + { + kind: "Event", + viewFn: newResourceView, + listFn: resource.NewEventList, + colorerFn: evColorer, + }, + { + kind: "ReplicationController", + viewFn: newScalableResourceView, + listFn: resource.NewReplicationControllerList, + colorerFn: rsColorer, }, - viewFn: newResourceView, - listFn: resource.NewNetworkPolicyList, } } -func batchRes(m map[string]resCmd) { - m["cj"] = resCmd{ - title: "CronJobs", - crdCmd: crdCmd{ - api: "batch", +func custRes() []*resCmd { + return []*resCmd{ + { + kind: "Users", + viewFn: newSubjectView, + }, + { + kind: "Groups", + viewFn: newSubjectView, + }, + { + kind: "PortForward", + viewFn: newForwardView, + }, + { + kind: "Benchmark", + viewFn: newBenchView, + }, + { + kind: "ScreenDumps", + viewFn: newDumpView, }, - viewFn: newCronJobView, - listFn: resource.NewCronJobList, } - m["jo"] = resCmd{ - title: "Jobs", - crdCmd: crdCmd{ - api: "batch", + +} + +func rbacRes() []*resCmd { + return []*resCmd{ + { + kind: "ClusterRole", + crdCmd: crdCmd{ + api: "rbac.authorization.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewClusterRoleList, + enterFn: showRBAC, + }, + { + kind: "ClusterRoleBinding", + crdCmd: crdCmd{ + api: "rbac.authorization.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewClusterRoleBindingList, + enterFn: showClusterRole, + }, + { + kind: "RoleBinding", + crdCmd: crdCmd{ + api: "rbac.authorization.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewRoleBindingList, + enterFn: showRole, + }, + { + kind: "Role", + crdCmd: crdCmd{ + api: "rbac.authorization.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewRoleList, + enterFn: showRBAC, }, - viewFn: newJobView, - listFn: resource.NewJobList, } } -func appsRes(m map[string]resCmd) { - m["dp"] = resCmd{ - title: "Deployments", - crdCmd: crdCmd{ - api: "apps", +func apiExtRes() []*resCmd { + return []*resCmd{ + { + kind: "CustomResourceDefinition", + crdCmd: crdCmd{ + api: "apiextensions.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewCustomResourceDefinitionList, + enterFn: showCRD, }, - viewFn: newDeployView, - listFn: resource.NewDeploymentList, - colorerFn: dpColorer, - } - m["rs"] = resCmd{ - title: "ReplicaSets", - crdCmd: crdCmd{ - api: "apps", + { + kind: "NetworkPolicy", + crdCmd: crdCmd{ + api: "apiextensions.k8s.io", + }, + viewFn: newResourceView, + listFn: resource.NewNetworkPolicyList, }, - 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", +func batchRes() []*resCmd { + return []*resCmd{ + { + kind: "CronJob", + crdCmd: crdCmd{ + api: "batch", + }, + viewFn: newCronJobView, + listFn: resource.NewCronJobList, + }, + { + kind: "Job", + crdCmd: crdCmd{ + api: "batch", + }, + viewFn: newJobView, + listFn: resource.NewJobList, }, - viewFn: newResourceView, - listFn: resource.NewIngressList, } } -func v1beta1Res(m map[string]resCmd) { - m["pdb"] = resCmd{ - title: "PodDisruptionBudgets", - crdCmd: crdCmd{ - api: "v1.beta1", +func appsRes() []*resCmd { + return []*resCmd{ + { + kind: "Deployment", + crdCmd: crdCmd{ + api: "apps", + }, + viewFn: newDeployView, + listFn: resource.NewDeploymentList, + colorerFn: dpColorer, + }, + { + kind: "ReplicaSet", + crdCmd: crdCmd{ + api: "apps", + }, + viewFn: newReplicaSetView, + listFn: resource.NewReplicaSetList, + colorerFn: rsColorer, + }, + { + kind: "StatefulSet", + crdCmd: crdCmd{ + api: "apps", + }, + viewFn: newStatefulSetView, + listFn: resource.NewStatefulSetList, + colorerFn: stsColorer, + }, + } + +} + +func extRes() []*resCmd { + return []*resCmd{ + { + kind: "Ingress", + crdCmd: crdCmd{ + api: "extensions", + }, + viewFn: newResourceView, + listFn: resource.NewIngressList, }, - viewFn: newResourceView, - listFn: resource.NewPDBList, - colorerFn: pdbColorer, } } -func hpaRes(c k8s.Connection, cmds map[string]resCmd) { +func v1beta1Res() []*resCmd { + return []*resCmd{ + { + kind: "PodDisruptionBudget", + crdCmd: crdCmd{ + api: "v1.beta1", + }, + viewFn: newResourceView, + listFn: resource.NewPDBList, + colorerFn: pdbColorer, + }, + } +} + +func hpaRes(c k8s.Connection) []*resCmd { rev, ok, err := c.SupportsRes("autoscaling", []string{"v1", "v2beta1", "v2beta2"}) if err != nil { log.Error().Err(err).Msg("Checking HPA") - return + return nil } if !ok { log.Error().Msg("HPA are not supported on this cluster") - return + return nil + } + + hpa := &resCmd{ + kind: "HorizontalPodAutoscaler", + crdCmd: crdCmd{ + api: "autoscaling", + }, + viewFn: newResourceView, } switch rev { case "v1": - cmds["hpa"] = resCmd{ - title: "HorizontalPodAutoscalers", - crdCmd: crdCmd{ - api: "autoscaling", - }, - viewFn: newResourceView, - listFn: resource.NewHorizontalPodAutoscalerV1List, - } + hpa.listFn = resource.NewHorizontalPodAutoscalerV1List case "v2beta1": - cmds["hpa"] = resCmd{ - title: "HorizontalPodAutoscalers", - crdCmd: crdCmd{ - api: "autoscaling", - }, - viewFn: newResourceView, - listFn: resource.NewHorizontalPodAutoscalerV2Beta1List, - } + hpa.listFn = resource.NewHorizontalPodAutoscalerV2Beta1List case "v2beta2": - cmds["hpa"] = resCmd{ - title: "HorizontalPodAutoscalers", - crdCmd: crdCmd{ - api: "autoscaling", - }, - viewFn: newResourceView, - listFn: resource.NewHorizontalPodAutoscalerList, - } + hpa.listFn = resource.NewHorizontalPodAutoscalerList default: log.Panic().Msgf("K9s unsupported HPA version. Exiting!") } + + return []*resCmd{hpa} }