checkpoint
parent
6839578a8c
commit
4127da865f
|
|
@ -299,8 +299,7 @@ issues:
|
|||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
|
||||
# Show only new issues created after git revision `REV`
|
||||
new-from-rev: REV
|
||||
# new-from-rev: REV
|
||||
# Show only new issues created in git patch with set file path.
|
||||
# new-from-patch: path/to/patch/file
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -10,7 +9,6 @@ import (
|
|||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/discovery/cached/disk"
|
||||
|
|
@ -26,53 +24,46 @@ const NA = "n/a"
|
|||
|
||||
var supportedMetricsAPIVersions = []string{"v1beta1"}
|
||||
|
||||
type (
|
||||
// Collection of empty interfaces.
|
||||
Collection []interface{}
|
||||
// Authorizer checks what a user can or cannot do to a resource.
|
||||
type Authorizer interface {
|
||||
// CanI returns true if the user can use these actions for a given resource.
|
||||
CanI(ns, gvr string, verbs []string) (bool, error)
|
||||
}
|
||||
|
||||
// Cruder represent a crudable Kubernetes resource.
|
||||
Cruder interface {
|
||||
Get(ns string, name string) (interface{}, error)
|
||||
List(ns string) (Collection, error)
|
||||
Delete(ns string, name string) error
|
||||
SetFieldSelector(string)
|
||||
SetLabelSelector(string)
|
||||
}
|
||||
// BOZO!! Refactor!
|
||||
// Connection represents a Kubenetes apiserver connection.
|
||||
type Connection interface {
|
||||
Authorizer
|
||||
|
||||
// Connection represents a Kubenetes apiserver connection.
|
||||
Connection interface {
|
||||
Config() *Config
|
||||
DialOrDie() kubernetes.Interface
|
||||
SwitchContextOrDie(ctx string)
|
||||
NSDialOrDie() dynamic.NamespaceableResourceInterface
|
||||
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||
RestConfigOrDie() *restclient.Config
|
||||
MXDial() (*versioned.Clientset, error)
|
||||
DynDialOrDie() dynamic.Interface
|
||||
HasMetrics() bool
|
||||
IsNamespaced(n string) bool
|
||||
SupportsResource(group string) bool
|
||||
ValidNamespaces() ([]v1.Namespace, error)
|
||||
NodePods(node string) (*v1.PodList, error)
|
||||
SupportsRes(grp string, versions []string) (string, bool, error)
|
||||
ServerVersion() (*version.Info, error)
|
||||
FetchNodes() (*v1.NodeList, error)
|
||||
CurrentNamespaceName() (string, error)
|
||||
CanI(ns, gvr string, verbs []string) (bool, error)
|
||||
}
|
||||
Config() *Config
|
||||
DialOrDie() kubernetes.Interface
|
||||
SwitchContextOrDie(ctx string)
|
||||
NSDialOrDie() dynamic.NamespaceableResourceInterface
|
||||
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||
RestConfigOrDie() *restclient.Config
|
||||
MXDial() (*versioned.Clientset, error)
|
||||
DynDialOrDie() dynamic.Interface
|
||||
HasMetrics() bool
|
||||
IsNamespaced(n string) bool
|
||||
SupportsResource(group string) bool
|
||||
ValidNamespaces() ([]v1.Namespace, error)
|
||||
SupportsRes(grp string, versions []string) (string, bool, error)
|
||||
ServerVersion() (*version.Info, error)
|
||||
FetchNodes() (*v1.NodeList, error)
|
||||
CurrentNamespaceName() (string, error)
|
||||
}
|
||||
|
||||
// APIClient represents a Kubernetes api client.
|
||||
APIClient struct {
|
||||
client kubernetes.Interface
|
||||
dClient dynamic.Interface
|
||||
nsClient dynamic.NamespaceableResourceInterface
|
||||
mxsClient *versioned.Clientset
|
||||
cachedDiscovery *disk.CachedDiscoveryClient
|
||||
config *Config
|
||||
useMetricServer bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
)
|
||||
// APIClient represents a Kubernetes api client.
|
||||
type APIClient struct {
|
||||
client kubernetes.Interface
|
||||
dClient dynamic.Interface
|
||||
nsClient dynamic.NamespaceableResourceInterface
|
||||
mxsClient *versioned.Clientset
|
||||
cachedDiscovery *disk.CachedDiscoveryClient
|
||||
config *Config
|
||||
useMetricServer bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
// InitConnectionOrDie initialize connection from command line args.
|
||||
// Checks for connectivity with the api server.
|
||||
|
|
@ -143,20 +134,6 @@ func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
|||
return nn.Items, nil
|
||||
}
|
||||
|
||||
// NodePods returns a collection of all available pods on a given node.
|
||||
func (a *APIClient) NodePods(node string) (*v1.PodList, error) {
|
||||
panic("NYI")
|
||||
const selFmt = "spec.nodeName=%s,status.phase!=%s,status.phase!=%s"
|
||||
fieldSelector, err := fields.ParseSelector(fmt.Sprintf(selFmt, node, v1.PodSucceeded, v1.PodFailed))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.DialOrDie().CoreV1().Pods("").List(metav1.ListOptions{
|
||||
FieldSelector: fieldSelector.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// IsNamespaced check on server if given resource is namespaced
|
||||
func (a *APIClient) IsNamespaced(res string) bool {
|
||||
discovery, err := a.CachedDiscovery()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -24,9 +23,6 @@ var _ Loggable = &Container{}
|
|||
|
||||
// Logs tails a given container logs
|
||||
func (c *Container) TailLogs(ctx context.Context, logChan chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("CO TAILLOGS %#v", ctx)
|
||||
log.Debug().Msgf("CO TAILLOGS %#v", opts)
|
||||
|
||||
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||
if !ok {
|
||||
return errors.New("Expecting an informer")
|
||||
|
|
@ -37,7 +33,7 @@ func (c *Container) TailLogs(ctx context.Context, logChan chan<- string, opts Lo
|
|||
}
|
||||
|
||||
var po v1.Pod
|
||||
if runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type Pod struct {
|
|||
}
|
||||
|
||||
var _ Accessor = &Pod{}
|
||||
var _Loggable = &Pod{}
|
||||
var _ Loggable = &Pod{}
|
||||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
|
|
@ -84,7 +84,7 @@ func (p *Pod) logs(ctx context.Context, c chan<- string, opts LogOptions) error
|
|||
}
|
||||
|
||||
var po v1.Pod
|
||||
if runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Color = asColor(po.Name)
|
||||
|
|
@ -166,6 +166,7 @@ func readLogs(ctx context.Context, stream io.ReadCloser, c chan<- string, opts L
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func loggableContainers(s v1.PodStatus) []string {
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ func Reconcile(ctx context.Context, table render.TableData, gvr client.GVR) (ren
|
|||
return table, err
|
||||
}
|
||||
log.Debug().Msgf("Model returned [%d] items", len(oo))
|
||||
|
||||
rows := make(render.Rows, len(oo))
|
||||
// BOZO!! Pass in header len to avoid recomputing the header.
|
||||
if err := m.Model.Hydrate(oo, rows, m.Renderer); err != nil {
|
||||
return table, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,83 +100,60 @@ func Load(f *watch.Factory) error {
|
|||
return loadCRDs(f, resMetas)
|
||||
}
|
||||
|
||||
// BOZO!! Need contermeasure for direct commands!
|
||||
func loadNonResource(m ResourceMetas) error {
|
||||
m["aliases"] = metav1.APIResource{
|
||||
Name: "aliases",
|
||||
SingularName: "alias",
|
||||
Namespaced: false,
|
||||
Kind: "Aliases",
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "aliases",
|
||||
Kind: "Aliases",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["contexts"] = metav1.APIResource{
|
||||
Name: "contexts",
|
||||
SingularName: "context",
|
||||
Namespaced: false,
|
||||
Kind: "Contexts",
|
||||
ShortNames: []string{"ctx"},
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "contexts",
|
||||
Kind: "Contexts",
|
||||
ShortNames: []string{"ctx"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["screendumps"] = metav1.APIResource{
|
||||
Name: "screendumps",
|
||||
SingularName: "screendump",
|
||||
Namespaced: false,
|
||||
Kind: "ScreenDumps",
|
||||
ShortNames: []string{"sd"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "screendumps",
|
||||
Kind: "ScreenDumps",
|
||||
ShortNames: []string{"sd"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["benchmarks"] = metav1.APIResource{
|
||||
Name: "benchmarks",
|
||||
SingularName: "benchmark",
|
||||
Namespaced: false,
|
||||
Kind: "Benchmarks",
|
||||
ShortNames: []string{"be"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "benchmarks",
|
||||
Kind: "Benchmarks",
|
||||
ShortNames: []string{"be"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["portforwards"] = metav1.APIResource{
|
||||
Name: "portforwards",
|
||||
SingularName: "portforward",
|
||||
Namespaced: true,
|
||||
Kind: "PortForwards",
|
||||
ShortNames: []string{"pf"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "portforwards",
|
||||
Namespaced: true,
|
||||
Kind: "PortForwards",
|
||||
ShortNames: []string{"pf"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
// BOZO!! policies can't be launch on command
|
||||
m["rbac"] = metav1.APIResource{
|
||||
Name: "Rbac",
|
||||
SingularName: "Rbac",
|
||||
Namespaced: false,
|
||||
Kind: "RBAC",
|
||||
Categories: []string{"k9s"},
|
||||
Name: "Rbac",
|
||||
Kind: "RBAC",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
// BOZO!! Containers can't be launch on command
|
||||
m["containers"] = metav1.APIResource{
|
||||
Name: "containers",
|
||||
SingularName: "container",
|
||||
Namespaced: false,
|
||||
Kind: "Containers",
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "containers",
|
||||
Kind: "Containers",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["users"] = metav1.APIResource{
|
||||
Name: "users",
|
||||
SingularName: "user",
|
||||
Namespaced: false,
|
||||
Kind: "User",
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "users",
|
||||
Kind: "User",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["groups"] = metav1.APIResource{
|
||||
Name: "groups",
|
||||
SingularName: "group",
|
||||
Namespaced: false,
|
||||
Kind: "group",
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
Name: "groups",
|
||||
Kind: "group",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -3,19 +3,19 @@ package internal
|
|||
// ContextKey represents context key.
|
||||
type ContextKey string
|
||||
|
||||
// A collection of context keys.
|
||||
const (
|
||||
// Factory represents a factory context key.
|
||||
KeyFactory ContextKey = "factory"
|
||||
KeyLabels = "labels"
|
||||
KeyFields = "fields"
|
||||
KeyTable = "table"
|
||||
KeyDir = "dir"
|
||||
KeyPath = "path"
|
||||
KeySubject = "subject"
|
||||
KeyGVR = "gvr"
|
||||
KeyForwards = "forwards"
|
||||
KeyContainers = "containers"
|
||||
KeyBenchCfg = "benchcfg"
|
||||
KeyAliases = "aliases"
|
||||
KeyUID = "uid"
|
||||
KeyLabels ContextKey = "labels"
|
||||
KeyFields ContextKey = "fields"
|
||||
KeyTable ContextKey = "table"
|
||||
KeyDir ContextKey = "dir"
|
||||
KeyPath ContextKey = "path"
|
||||
KeySubject ContextKey = "subject"
|
||||
KeyGVR ContextKey = "gvr"
|
||||
KeyForwards ContextKey = "forwards"
|
||||
KeyContainers ContextKey = "containers"
|
||||
KeyBenchCfg ContextKey = "benchcfg"
|
||||
KeyAliases ContextKey = "aliases"
|
||||
KeyUID ContextKey = "uid"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -68,8 +68,11 @@ func (c *Container) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) er
|
|||
|
||||
var index int
|
||||
for _, o := range oo {
|
||||
co := o.(ContainerRes)
|
||||
row, err := renderCoRow(co.Container.Name, index, coMetricsFor(co.Container, c.pod, mmx, true), re)
|
||||
co, ok := o.(ContainerRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting containerres but got `%T", o)
|
||||
}
|
||||
row, err := renderCoRow(co.Container.Name, coMetricsFor(co.Container, c.pod, mmx, true), re)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -80,7 +83,7 @@ func (c *Container) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func renderCoRow(n string, index int, pmx *ContainerWithMetrics, re Renderer) (render.Row, error) {
|
||||
func renderCoRow(n string, pmx *ContainerWithMetrics, re Renderer) (render.Row, error) {
|
||||
var row render.Row
|
||||
if err := re.Render(pmx, n, &row); err != nil {
|
||||
return render.Row{}, err
|
||||
|
|
@ -99,7 +102,11 @@ func coMetricsFor(co v1.Container, po *v1.Pod, mmx *mv1beta1.PodMetrics, isInit
|
|||
}
|
||||
|
||||
func containerMetrics(n string, mx runtime.Object) *mv1beta1.ContainerMetrics {
|
||||
pmx := mx.(*mv1beta1.PodMetrics)
|
||||
pmx, ok := mx.(*mv1beta1.PodMetrics)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting podmetrics but got `%T", mx))
|
||||
return nil
|
||||
}
|
||||
for _, m := range pmx.Containers {
|
||||
if m.Name == n {
|
||||
return &m
|
||||
|
|
|
|||
|
|
@ -71,10 +71,6 @@ func (g *Generic) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) erro
|
|||
if !ok {
|
||||
return fmt.Errorf("expecting RowRes but got %#v", o)
|
||||
}
|
||||
count := len(res.Cells)
|
||||
if g.namespace == "" {
|
||||
count++
|
||||
}
|
||||
if err := gr.Render(res.TableRow, g.namespace, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,39 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func extractFQN(o runtime.Object) string {
|
||||
u := o.(*unstructured.Unstructured)
|
||||
m := u.Object["metadata"].(map[string]interface{})
|
||||
if _, ok := m["namespace"]; !ok {
|
||||
return FQN("", m["name"].(string))
|
||||
u, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting unstructured but got %T", o))
|
||||
return "na"
|
||||
}
|
||||
ns, n := m["namespace"].(string), m["name"].(string)
|
||||
m, ok := u.Object["metadata"].(map[string]interface{})
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting interface map for metadata but got %T", u.Object["metadata"]))
|
||||
return "na"
|
||||
}
|
||||
|
||||
n, ok := m["name"].(string)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting interface map for name but got %T", m["name"]))
|
||||
return "na"
|
||||
}
|
||||
|
||||
ns, ok := m["namespace"].(string)
|
||||
if !ok {
|
||||
return FQN("", n)
|
||||
}
|
||||
|
||||
return FQN(ns, n)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
|
|
@ -29,8 +30,8 @@ func (n *Node) List(_ context.Context) ([]runtime.Object, error) {
|
|||
}
|
||||
|
||||
oo := make([]runtime.Object, len(nn.Items))
|
||||
for i, no := range nn.Items {
|
||||
o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&no)
|
||||
for i, n := range nn.Items {
|
||||
o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -39,6 +40,20 @@ func (n *Node) List(_ context.Context) ([]runtime.Object, error) {
|
|||
return oo, nil
|
||||
}
|
||||
|
||||
func nameFromMeta(m map[string]interface{}) string {
|
||||
meta, ok := m["metadata"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
name, ok := meta["name"].(string)
|
||||
if !ok {
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// Hydrate returns nodes as rows.
|
||||
func (n *Node) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
mx := client.NewMetricsServer(n.factory.Client())
|
||||
|
|
@ -47,23 +62,28 @@ func (n *Node) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
|||
log.Warn().Err(err).Msg("No node metrics")
|
||||
}
|
||||
|
||||
var index int
|
||||
for _, no := range oo {
|
||||
o := no.(*unstructured.Unstructured)
|
||||
pods, err := n.nodePods(n.factory, o.Object["metadata"].(map[string]interface{})["name"].(string))
|
||||
for i, o := range oo {
|
||||
no, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting unstructured but got %T", o)
|
||||
}
|
||||
pods, err := n.nodePods(n.factory, nameFromMeta(no.Object))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
row render.Row
|
||||
nmx = NodeWithMetrics{object: o, mx: nodeMetricsFor(o, mmx), pods: pods}
|
||||
nmx = NodeWithMetrics{
|
||||
object: no,
|
||||
mx: nodeMetricsFor(o, mmx),
|
||||
pods: pods,
|
||||
}
|
||||
)
|
||||
if err := re.Render(&nmx, "", &row); err != nil {
|
||||
return err
|
||||
}
|
||||
rr[index] = row
|
||||
index++
|
||||
rr[i] = row
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -87,8 +107,10 @@ func (n *Node) nodePods(f Factory, node string) ([]*v1.Pod, error) {
|
|||
|
||||
pods := make([]*v1.Pod, 0, len(pp))
|
||||
for _, p := range pp {
|
||||
o := p.(*unstructured.Unstructured)
|
||||
|
||||
o, ok := p.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expecting unstructured but got %T", p)
|
||||
}
|
||||
var pod v1.Pod
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &pod)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
|
|
@ -37,8 +38,14 @@ func (p *Pod) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
|
||||
var res []runtime.Object
|
||||
for _, o := range oo {
|
||||
u := o.(*unstructured.Unstructured)
|
||||
spec := u.Object["spec"].(map[string]interface{})
|
||||
u, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return res, fmt.Errorf("expecting unstructured but got `%T", o)
|
||||
}
|
||||
spec, ok := u.Object["spec"].(map[string]interface{})
|
||||
if !ok {
|
||||
return res, fmt.Errorf("expecting interface map but got `%T", o)
|
||||
}
|
||||
if nodeName == "" || spec["nodeName"] == nodeName {
|
||||
res = append(res, o)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,12 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// PortForward represents a portforward model.
|
||||
type PortForward struct {
|
||||
Resource
|
||||
|
||||
pod *v1.Pod
|
||||
}
|
||||
|
||||
// List returns a collection of screen dumps.
|
||||
|
|
@ -51,7 +47,6 @@ func (c *PortForward) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
// Hydrate returns a pod as container rows.
|
||||
func (c *PortForward) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
log.Debug().Msgf("PortFWD GOT %#v", o)
|
||||
res, ok := o.(render.ForwardRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting a forwardres but got %T", o)
|
||||
|
|
|
|||
|
|
@ -76,16 +76,15 @@ func (r *Rbac) loadRoleBinding(path string) ([]runtime.Object, error) {
|
|||
}
|
||||
|
||||
var rb rbacv1.RoleBinding
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
||||
if err != nil {
|
||||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rb.RoleRef.Kind == "ClusterRole" {
|
||||
kind := "rbac.authorization.k8s.io/v1/clusterroles"
|
||||
o, err := r.factory.Get(kind, client.FQN("-", rb.RoleRef.Name), labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
o, e := r.factory.Get(kind, client.FQN("-", rb.RoleRef.Name), labels.Everything())
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
var cr rbacv1.ClusterRole
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
||||
|
|
@ -186,7 +185,11 @@ func upsert(rr []runtime.Object, p *render.PolicyRes) []runtime.Object {
|
|||
// Find locates a row by id. Retturns false is not found.
|
||||
func find(rr []runtime.Object, res string) (int, bool) {
|
||||
for i, r := range rr {
|
||||
p := r.(*render.PolicyRes)
|
||||
p, ok := r.(*render.PolicyRes)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting policyres but got `%T", r))
|
||||
return 0, false
|
||||
}
|
||||
if p.Resource == res {
|
||||
return i, true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,12 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// ScreenDump represents a collections of screendumps.
|
||||
type ScreenDump struct {
|
||||
Resource
|
||||
|
||||
pod *v1.Pod
|
||||
}
|
||||
|
||||
// List returns a collection of screen dumps.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -58,58 +57,59 @@ func (s *Subject) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Subject) fetchClusterRoleBindings() ([]runtime.Object, error) {
|
||||
oo, err := s.factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterrolebindings", labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// BOZO!!
|
||||
// func (s *Subject) fetchClusterRoleBindings() ([]runtime.Object, error) {
|
||||
// oo, err := s.factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterrolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
rows := make([]runtime.Object, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var crb rbacv1.ClusterRoleBinding
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, subject := range crb.Subjects {
|
||||
if subject.Kind != s.subjectKind {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, SubjectRes{
|
||||
id: subject.Name,
|
||||
fields: render.Fields{subject.Name, "ClusterRoleBinding", crb.Name},
|
||||
})
|
||||
}
|
||||
}
|
||||
// rows := make([]runtime.Object, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var crb rbacv1.ClusterRoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, subject := range crb.Subjects {
|
||||
// if subject.Kind != s.subjectKind {
|
||||
// continue
|
||||
// }
|
||||
// rows = append(rows, SubjectRes{
|
||||
// id: subject.Name,
|
||||
// fields: render.Fields{subject.Name, "ClusterRoleBinding", crb.Name},
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
// return rows, nil
|
||||
// }
|
||||
|
||||
func (s *Subject) fetchRoleBindings() ([]runtime.Object, error) {
|
||||
oo, err := s.factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/rolebindings", labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// func (s *Subject) fetchRoleBindings() ([]runtime.Object, error) {
|
||||
// oo, err := s.factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/rolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
rows := make([]runtime.Object, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var rb rbacv1.RoleBinding
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, subject := range rb.Subjects {
|
||||
if subject.Kind == s.subjectKind {
|
||||
rows = append(rows, SubjectRes{
|
||||
id: subject.Name,
|
||||
fields: render.Fields{subject.Name, "RoleBinding", rb.Name},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// rows := make([]runtime.Object, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var rb rbacv1.RoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, subject := range rb.Subjects {
|
||||
// if subject.Kind == s.subjectKind {
|
||||
// rows = append(rows, SubjectRes{
|
||||
// id: subject.Name,
|
||||
// fields: render.Fields{subject.Name, "RoleBinding", rb.Name},
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
// return rows, nil
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ type Lister interface {
|
|||
List(context.Context) ([]runtime.Object, error)
|
||||
|
||||
// Hydrate converts resource rows into tabular data.
|
||||
Hydrate([]runtime.Object, render.Rows, Renderer) error
|
||||
Hydrate(oo []runtime.Object, rr render.Rows, r Renderer) error
|
||||
}
|
||||
|
||||
type Factory interface {
|
||||
|
|
|
|||
|
|
@ -10,31 +10,22 @@ package render
|
|||
// colorerUCs []colorerUC
|
||||
// )
|
||||
|
||||
// func TestNSColorer(t *testing.T) {
|
||||
// var (
|
||||
// ns = Row{Fields: Fields{"blee", "Active"}}
|
||||
// term = Row{Fields: Fields{"blee", Terminating}}
|
||||
// dead = Row{Fields: Fields{"blee", "Inactive"}}
|
||||
// )
|
||||
|
||||
// uu := colorerUCs{
|
||||
// // Add AllNS
|
||||
// {"", RowEvent{
|
||||
// Kind: EventAdd,
|
||||
// Row: ns,
|
||||
// },
|
||||
// AddColor},
|
||||
// // Mod AllNS
|
||||
// {"", RowEvent{Kind: EventUpdate, Row: ns}, ModColor},
|
||||
// // MoChange AllNS
|
||||
// {"", RowEvent{Kind: EventUnchanged, Row: ns}, StdColor},
|
||||
// // Bust NS
|
||||
// {"", RowEvent{Kind: EventUnchanged, Row: term}, ErrColor},
|
||||
// // Bust NS
|
||||
// {"", RowEvent{Kind: EventUnchanged, Row: dead}, ErrColor},
|
||||
// func TestDefaultColorer(t *testing.T) {
|
||||
// uu := map[string]struct {
|
||||
// re render.RowEvent
|
||||
// e tcell.Color
|
||||
// }{
|
||||
// "default": {render.RowEvent{}, ui.StdColor},
|
||||
// "add": {render.RowEvent{Kind: render.EventAdd}, ui.AddColor},
|
||||
// "delete": {render.RowEvent{Kind: render.EventDelete}, ui.KillColor},
|
||||
// "update": {render.RowEvent{Kind: render.EventUpdate}, ui.ModColor},
|
||||
// }
|
||||
// for _, u := range uu {
|
||||
// assert.Equal(t, u.e, nsColorer(u.ns, u.r))
|
||||
|
||||
// for k := range uu {
|
||||
// u := uu[k]
|
||||
// t.Run(k, func(t *testing.T) {
|
||||
// assert.Equal(t, u.e, ui.DefaultColorer("", u.re))
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (c, p metric
|
|||
mem: ToMi(mem),
|
||||
}
|
||||
|
||||
rcpu, rmem := containerResources(co)
|
||||
rcpu, rmem := containerResources(*co)
|
||||
if rcpu != nil {
|
||||
p.cpu = AsPerc(toPerc(float64(cpu), float64(rcpu.MilliValue())))
|
||||
}
|
||||
|
|
@ -177,19 +177,9 @@ func toState(s v1.ContainerState) string {
|
|||
}
|
||||
}
|
||||
|
||||
func toRes(r v1.ResourceList) (string, string) {
|
||||
cpu, mem := r[v1.ResourceCPU], r[v1.ResourceMemory]
|
||||
|
||||
return ToMillicore(cpu.MilliValue()), ToMi(ToMB(mem.Value()))
|
||||
}
|
||||
|
||||
func probe(p *v1.Probe) string {
|
||||
if p == nil {
|
||||
return "off"
|
||||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
func asMi(v int64) float64 {
|
||||
return float64(v) / 1024 * 1024
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,27 +38,21 @@ func TestContextRender(t *testing.T) {
|
|||
}
|
||||
|
||||
var r render.Context
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
row := render.NewRow(4)
|
||||
err := r.Render(u.ctx, "", &row)
|
||||
err := r.Render(uc.ctx, "", &row)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, u.e, row)
|
||||
assert.Equal(t, uc.e, row)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func newContext(n string) *api.Context {
|
||||
return &api.Context{
|
||||
Cluster: n,
|
||||
AuthInfo: "blee",
|
||||
Namespace: "zorg",
|
||||
}
|
||||
}
|
||||
|
||||
type config struct{}
|
||||
|
||||
func (k config) CurrentContextName() (string, error) {
|
||||
|
|
|
|||
|
|
@ -32,17 +32,36 @@ func (CustomResourceDefinition) Render(o interface{}, ns string, r *Row) error {
|
|||
return fmt.Errorf("Expected CustomResourceDefinition, but got %T", o)
|
||||
}
|
||||
|
||||
meta := crd.Object["metadata"].(map[string]interface{})
|
||||
t, err := time.Parse(time.RFC3339, meta["creationTimestamp"].(string))
|
||||
meta, ok := crd.Object["metadata"].(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting an interface map but got %T", crd.Object["metadata"])
|
||||
}
|
||||
t, err := time.Parse(time.RFC3339, extractMetaField(meta, "creationTimestamp"))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Fields timestamp %v", err)
|
||||
}
|
||||
|
||||
r.ID = FQN(ClusterScope, meta["name"].(string))
|
||||
r.ID = FQN(ClusterScope, extractMetaField(meta, "name"))
|
||||
r.Fields = Fields{
|
||||
meta["name"].(string),
|
||||
toAge(metav1.Time{t}),
|
||||
extractMetaField(meta, "name"),
|
||||
toAge(metav1.Time{Time: t}),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractMetaField(m map[string]interface{}, field string) string {
|
||||
f, ok := m[field]
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("failed to extract field from meta %s", field))
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
fs, ok := f.(string)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("failed to extract string from field %s", field))
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,11 +53,12 @@ func TestDelta(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
d := render.NewDeltaRow(u.o, u.n, false)
|
||||
assert.Equal(t, u.e, d)
|
||||
assert.Equal(t, u.blank, d.IsBlank())
|
||||
d := render.NewDeltaRow(uc.o, uc.n, false)
|
||||
assert.Equal(t, uc.e, d)
|
||||
assert.Equal(t, uc.blank, d.IsBlank())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -80,9 +81,10 @@ func TestDeltaBlank(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, u.r.IsBlank())
|
||||
assert.Equal(t, uc.e, uc.r.IsBlank())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func (Endpoints) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Endpoints) Render(o interface{}, ns string, r *Row) error {
|
||||
func (e Endpoints) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected Endpoints, but got %T", o)
|
||||
|
|
@ -44,16 +44,16 @@ func (Endpoints) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(ep.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(e.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, ep.Namespace)
|
||||
r.Fields = append(r.Fields, ep.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
ep.Name,
|
||||
missing(toEPs(ep.Subsets)),
|
||||
toAge(ep.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(ep.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (Event) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Event) Render(o interface{}, ns string, r *Row) error {
|
||||
func (e Event) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected Event, but got %T", o)
|
||||
|
|
@ -64,18 +64,18 @@ func (Event) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(ev.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(e.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, ev.Namespace)
|
||||
r.Fields = append(r.Fields, ev.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
ev.Name,
|
||||
ev.Reason,
|
||||
ev.Source.Component,
|
||||
strconv.Itoa(int(ev.Count)),
|
||||
Truncate(ev.Message, 80),
|
||||
toAge(ev.LastTimestamp))
|
||||
r.ID, r.Fields = MetaFQN(ev.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,11 @@ func TestSort(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
u.re.Sort("", u.col, u.asc)
|
||||
assert.Equal(t, u.e, u.re)
|
||||
uc.re.Sort("", uc.col, uc.asc)
|
||||
assert.Equal(t, uc.e, uc.re)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -50,9 +51,10 @@ func TestDefaultColorer(t *testing.T) {
|
|||
"std": {100, render.StdColor},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, render.DefaultColorer("", render.RowEvent{}))
|
||||
assert.Equal(t, uc.e, render.DefaultColorer("", render.RowEvent{}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -49,9 +48,10 @@ func TestToAge(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, toAge(metav1.Time{Time: u.t})[:2])
|
||||
assert.Equal(t, uc.e, toAge(metav1.Time{Time: uc.t})[:2])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -67,10 +67,11 @@ func TestToAgeHuma(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
ti := toAge(metav1.Time{Time: u.t})
|
||||
assert.Equal(t, u.e, toAgeHuman(ti)[:2])
|
||||
ti := toAge(metav1.Time{Time: uc.t})
|
||||
assert.Equal(t, uc.e, toAgeHuman(ti)[:2])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -86,9 +87,10 @@ func TestJoin(t *testing.T) {
|
|||
"sparse": {[]string{"a", "", "c"}, "a,c"},
|
||||
}
|
||||
|
||||
for k, v := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, v.e, join(v.i, ","))
|
||||
assert.Equal(t, uc.e, join(uc.i, ","))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -195,11 +197,12 @@ func TestToSelector(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
s := toSelector(u.m)
|
||||
s := toSelector(uc.m)
|
||||
var match bool
|
||||
for _, e := range u.e {
|
||||
for _, e := range uc.e {
|
||||
if e == s {
|
||||
match = true
|
||||
}
|
||||
|
|
@ -225,9 +228,10 @@ func TestBlank(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, blank(u.a))
|
||||
assert.Equal(t, uc.e, blank(uc.a))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -252,9 +256,10 @@ func TestIn(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, in(u.a, u.v))
|
||||
assert.Equal(t, uc.e, in(uc.a, uc.v))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -268,9 +273,10 @@ func TestMetaFQN(t *testing.T) {
|
|||
"nons": {metav1.ObjectMeta{Name: "blee"}, "blee"},
|
||||
}
|
||||
|
||||
for k, v := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, v.e, MetaFQN(v.m))
|
||||
assert.Equal(t, uc.e, MetaFQN(uc.m))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -284,9 +290,10 @@ func TestFQN(t *testing.T) {
|
|||
"nons": {n: "blee", e: "blee"},
|
||||
}
|
||||
|
||||
for k, v := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, v.e, FQN(v.ns, v.n))
|
||||
assert.Equal(t, uc.e, FQN(uc.ns, uc.n))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -374,10 +381,11 @@ func BenchmarkAsPerc(b *testing.B) {
|
|||
|
||||
// Helpers...
|
||||
|
||||
func testTime() time.Time {
|
||||
t, err := time.Parse(time.RFC3339, "2018-12-14T10:36:43.326972-07:00")
|
||||
if err != nil {
|
||||
fmt.Println("TestTime Failed", err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
// BOZO!!
|
||||
// func testTime() time.Time {
|
||||
// t, err := time.Parse(time.RFC3339, "2018-12-14T10:36:43.326972-07:00")
|
||||
// if err != nil {
|
||||
// fmt.Println("TestTime Failed", err)
|
||||
// }
|
||||
// return t
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func (HorizontalPodAutoscaler) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error {
|
||||
func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected HorizontalPodAutoscaler, but got %T", o)
|
||||
|
|
@ -48,11 +48,12 @@ func (HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(hpa.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(h.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, hpa.Namespace)
|
||||
r.Fields = append(r.Fields, hpa.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
hpa.ObjectMeta.Name,
|
||||
hpa.Spec.ScaleTargetRef.Name,
|
||||
toMetrics(hpa.Spec, hpa.Status),
|
||||
|
|
@ -61,7 +62,6 @@ func (HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error {
|
|||
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
||||
toAge(hpa.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(hpa.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
|
|
@ -77,8 +76,9 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
|
|||
ro := make([]string, 10)
|
||||
nodeRoles(&no, ro)
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
fields = append(fields,
|
||||
r.ID = MetaFQN(no.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(n.Header(ns)))
|
||||
r.Fields = append(r.Fields,
|
||||
no.Name,
|
||||
join(sta, ","),
|
||||
join(ro, ","),
|
||||
|
|
@ -94,11 +94,8 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
|
|||
a.mem,
|
||||
toAge(no.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID = MetaFQN(no.ObjectMeta)
|
||||
r.Fields = fields
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -132,10 +129,6 @@ func gatherNodeMX(no *v1.Node, mx *mv1beta1.NodeMetrics) (c metric, a metric, p
|
|||
return
|
||||
}
|
||||
|
||||
func withPerc(v, p string) string {
|
||||
return v + " (" + p + ")"
|
||||
}
|
||||
|
||||
func nodeRoles(node *v1.Node, res []string) {
|
||||
index := 0
|
||||
for k, v := range node.Labels {
|
||||
|
|
@ -200,87 +193,6 @@ func status(status v1.NodeStatus, exempt bool, res []string) {
|
|||
}
|
||||
}
|
||||
|
||||
func findNodeRoles(no *v1.Node) []string {
|
||||
roles := sets.NewString()
|
||||
for k, v := range no.Labels {
|
||||
switch {
|
||||
case strings.HasPrefix(k, labelNodeRolePrefix):
|
||||
if role := strings.TrimPrefix(k, labelNodeRolePrefix); len(role) > 0 {
|
||||
roles.Insert(role)
|
||||
}
|
||||
case k == nodeLabelRole && v != "":
|
||||
roles.Insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
return roles.List()
|
||||
}
|
||||
|
||||
func podsResources(name string, pods []*v1.Pod) (v1.ResourceList, v1.ResourceList, error) {
|
||||
reqs, limits := v1.ResourceList{}, v1.ResourceList{}
|
||||
for _, p := range pods {
|
||||
preq, plim := podResources(p)
|
||||
for k, v := range preq {
|
||||
if value, ok := reqs[k]; !ok {
|
||||
reqs[k] = v.DeepCopy()
|
||||
} else {
|
||||
value.Add(v)
|
||||
reqs[k] = value
|
||||
}
|
||||
}
|
||||
for k, v := range plim {
|
||||
if value, ok := limits[k]; !ok {
|
||||
limits[k] = v.DeepCopy()
|
||||
} else {
|
||||
value.Add(v)
|
||||
limits[k] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reqs, limits, nil
|
||||
}
|
||||
|
||||
func podResources(pod *v1.Pod) (v1.ResourceList, v1.ResourceList) {
|
||||
reqs, limits := v1.ResourceList{}, v1.ResourceList{}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
addResources(reqs, container.Resources.Requests)
|
||||
addResources(limits, container.Resources.Limits)
|
||||
}
|
||||
// init containers define the minimum of any resource
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
maxResources(reqs, container.Resources.Requests)
|
||||
maxResources(limits, container.Resources.Limits)
|
||||
}
|
||||
|
||||
return reqs, limits
|
||||
}
|
||||
|
||||
// AddResources adds the resources from l2 to l1.
|
||||
func addResources(l1, l2 v1.ResourceList) {
|
||||
for name, quantity := range l2 {
|
||||
if value, ok := l1[name]; ok {
|
||||
value.Add(quantity)
|
||||
l1[name] = value
|
||||
} else {
|
||||
l1[name] = quantity.DeepCopy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MaxResourceList sets list to the greater of l1/l2 for every resource.
|
||||
func maxResources(l1, l2 v1.ResourceList) {
|
||||
for name, quantity := range l2 {
|
||||
if value, ok := l1[name]; ok {
|
||||
if quantity.Cmp(value) > 0 {
|
||||
l1[name] = quantity.DeepCopy()
|
||||
}
|
||||
} else {
|
||||
l1[name] = quantity.DeepCopy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func empty(s []string) bool {
|
||||
for _, v := range s {
|
||||
if len(v) != 0 {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func (NetworkPolicy) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (NetworkPolicy) Render(o interface{}, ns string, r *Row) error {
|
||||
func (n NetworkPolicy) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected NetworkPolicy, but got %T", o)
|
||||
|
|
@ -52,11 +52,12 @@ func (NetworkPolicy) Render(o interface{}, ns string, r *Row) error {
|
|||
ip, is, ib := ingress(np.Spec.Ingress)
|
||||
ep, es, eb := egress(np.Spec.Egress)
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(np.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(n.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, np.Namespace)
|
||||
r.Fields = append(r.Fields, np.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
np.Name,
|
||||
is,
|
||||
ip,
|
||||
|
|
@ -66,7 +67,6 @@ func (NetworkPolicy) Render(o interface{}, ns string, r *Row) error {
|
|||
eb,
|
||||
toAge(np.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(np.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,33 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNSColorer(t *testing.T) {
|
||||
var (
|
||||
ns = render.Row{Fields: render.Fields{"blee", "Active"}}
|
||||
term = render.Row{Fields: render.Fields{"blee", render.Terminating}}
|
||||
dead = render.Row{Fields: render.Fields{"blee", "Inactive"}}
|
||||
)
|
||||
|
||||
uu := colorerUCs{
|
||||
// Add AllNS
|
||||
{"", render.RowEvent{Kind: render.EventAdd, Row: ns}, render.AddColor},
|
||||
// Mod AllNS
|
||||
{"", render.RowEvent{Kind: render.EventUpdate, Row: ns}, render.ModColor},
|
||||
// MoChange AllNS
|
||||
{"", render.RowEvent{Kind: render.EventUnchanged, Row: ns}, render.StdColor},
|
||||
// Bust NS
|
||||
{"", render.RowEvent{Kind: render.EventUnchanged, Row: term}, render.ErrColor},
|
||||
// Bust NS
|
||||
{"", render.RowEvent{Kind: render.EventUnchanged, Row: dead}, render.ErrColor},
|
||||
}
|
||||
|
||||
var n render.Namespace
|
||||
f := n.ColorerFunc()
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, f(u.ns, u.r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceRender(t *testing.T) {
|
||||
c := render.Namespace{}
|
||||
r := render.NewRow(3)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func (PodDisruptionBudget) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (PodDisruptionBudget) Render(o interface{}, ns string, r *Row) error {
|
||||
func (p PodDisruptionBudget) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected PodDisruptionBudget, but got %T", o)
|
||||
|
|
@ -68,11 +68,12 @@ func (PodDisruptionBudget) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(pdb.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(p.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, pdb.Namespace)
|
||||
r.Fields = append(r.Fields, pdb.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
pdb.Name,
|
||||
numbToStr(pdb.Spec.MinAvailable),
|
||||
numbToStr(pdb.Spec.MaxUnavailable),
|
||||
|
|
@ -82,7 +83,6 @@ func (PodDisruptionBudget) Render(o interface{}, ns string, r *Row) error {
|
|||
strconv.Itoa(int(pdb.Status.ExpectedPods)),
|
||||
toAge(pdb.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(pdb.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
@ -158,7 +157,7 @@ func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (c, p metric) {
|
|||
return
|
||||
}
|
||||
|
||||
func containerResources(co *v1.Container) (cpu, mem *resource.Quantity) {
|
||||
func containerResources(co v1.Container) (cpu, mem *resource.Quantity) {
|
||||
req, limit := co.Resources.Requests, co.Resources.Limits
|
||||
switch {
|
||||
case len(req) != 0:
|
||||
|
|
@ -171,7 +170,7 @@ func containerResources(co *v1.Container) (cpu, mem *resource.Quantity) {
|
|||
|
||||
func requestedRes(po *v1.Pod) (cpu, mem resource.Quantity) {
|
||||
for _, co := range po.Spec.Containers {
|
||||
c, m := containerResources(&co)
|
||||
c, m := containerResources(co)
|
||||
if c != nil {
|
||||
cpu.Add(*c)
|
||||
}
|
||||
|
|
@ -225,13 +224,13 @@ func (p *Pod) phase(po *v1.Pod) string {
|
|||
status = po.Status.Reason
|
||||
}
|
||||
|
||||
init, status := p.initContainerPhase(po.Status, len(po.Spec.InitContainers), status)
|
||||
if init {
|
||||
status, ok := p.initContainerPhase(po.Status, len(po.Spec.InitContainers), status)
|
||||
if ok {
|
||||
return status
|
||||
}
|
||||
|
||||
running, status := p.containerPhase(po.Status, status)
|
||||
if running && status == "Completed" {
|
||||
status, ok = p.containerPhase(po.Status, status)
|
||||
if ok && status == "Completed" {
|
||||
status = "Running"
|
||||
}
|
||||
if po.DeletionTimestamp == nil {
|
||||
|
|
@ -241,7 +240,7 @@ func (p *Pod) phase(po *v1.Pod) string {
|
|||
return "Terminated"
|
||||
}
|
||||
|
||||
func (*Pod) containerPhase(st v1.PodStatus, status string) (bool, string) {
|
||||
func (*Pod) containerPhase(st v1.PodStatus, status string) (string, bool) {
|
||||
var running bool
|
||||
for i := len(st.ContainerStatuses) - 1; i >= 0; i-- {
|
||||
cs := st.ContainerStatuses[i]
|
||||
|
|
@ -261,29 +260,22 @@ func (*Pod) containerPhase(st v1.PodStatus, status string) (bool, string) {
|
|||
}
|
||||
}
|
||||
|
||||
return running, status
|
||||
return status, running
|
||||
}
|
||||
|
||||
func (*Pod) initContainerPhase(st v1.PodStatus, initCount int, status string) (bool, string) {
|
||||
func (*Pod) initContainerPhase(st v1.PodStatus, initCount int, status string) (string, bool) {
|
||||
for i, cs := range st.InitContainerStatuses {
|
||||
status := checkContainerStatus(cs, i, initCount)
|
||||
if status == "" {
|
||||
s := checkContainerStatus(cs, i, initCount)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
return true, status
|
||||
return s, true
|
||||
}
|
||||
|
||||
return false, status
|
||||
}
|
||||
|
||||
func (*Pod) loggableContainers(s v1.PodStatus) []string {
|
||||
var rcos []string
|
||||
for _, c := range s.ContainerStatuses {
|
||||
rcos = append(rcos, c.Name)
|
||||
}
|
||||
return rcos
|
||||
return status, false
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers..
|
||||
|
||||
func checkContainerStatus(cs v1.ContainerStatus, i, initCount int) string {
|
||||
|
|
@ -305,15 +297,3 @@ func checkContainerStatus(cs v1.ContainerStatus, i, initCount int) string {
|
|||
return "Init:" + strconv.Itoa(i) + "/" + strconv.Itoa(initCount)
|
||||
}
|
||||
}
|
||||
|
||||
func asColor(n string) color.Paint {
|
||||
var sum int
|
||||
for _, r := range n {
|
||||
sum += int(r)
|
||||
}
|
||||
return color.Paint(30 + 2 + sum%6)
|
||||
}
|
||||
|
||||
func isSet(s *string) bool {
|
||||
return s != nil && *s != ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
res "k8s.io/apimachinery/pkg/api/resource"
|
||||
|
|
@ -13,6 +14,52 @@ import (
|
|||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
type (
|
||||
colorerUC struct {
|
||||
ns string
|
||||
r render.RowEvent
|
||||
e tcell.Color
|
||||
}
|
||||
|
||||
colorerUCs []colorerUC
|
||||
)
|
||||
|
||||
func TestPodColorer(t *testing.T) {
|
||||
var (
|
||||
nsRow = render.Row{Fields: render.Fields{"blee", "fred", "1/1", "Running"}}
|
||||
toastNS = render.Row{Fields: render.Fields{"blee", "fred", "1/1", "Boom"}}
|
||||
notReadyNS = render.Row{Fields: render.Fields{"blee", "fred", "0/1", "Boom"}}
|
||||
row = render.Row{Fields: render.Fields{"fred", "1/1", "Running"}}
|
||||
toast = render.Row{Fields: render.Fields{"fred", "1/1", "Boom"}}
|
||||
notReady = render.Row{Fields: render.Fields{"fred", "0/1", "Boom"}}
|
||||
)
|
||||
|
||||
uu := colorerUCs{
|
||||
// Add allNS
|
||||
{"", render.RowEvent{Kind: render.EventAdd, Row: nsRow}, render.AddColor},
|
||||
// Add Namespaced
|
||||
{"blee", render.RowEvent{Kind: render.EventAdd, Row: row}, render.AddColor},
|
||||
// Mod AllNS
|
||||
{"", render.RowEvent{Kind: render.EventUpdate, Row: nsRow}, render.ModColor},
|
||||
// Mod Namespaced
|
||||
{"blee", render.RowEvent{Kind: render.EventUpdate, Row: row}, render.ModColor},
|
||||
// Mod Busted AllNS
|
||||
{"", render.RowEvent{Kind: render.EventUpdate, Row: toastNS}, render.ErrColor},
|
||||
// Mod Busted Namespaced
|
||||
{"blee", render.RowEvent{Kind: render.EventUpdate, Row: toast}, render.ErrColor},
|
||||
// NotReady AllNS
|
||||
{"", render.RowEvent{Kind: render.EventUpdate, Row: notReadyNS}, render.ErrColor},
|
||||
// NotReady Namespaced
|
||||
{"blee", render.RowEvent{Kind: render.EventUpdate, Row: notReady}, render.ErrColor},
|
||||
}
|
||||
|
||||
var p render.Pod
|
||||
f := p.ColorerFunc()
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, f(u.ns, u.r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodRender(t *testing.T) {
|
||||
pom := podMetrics{load(t, "po"), makePodMX("nginx", "10m", "10Mi")}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,5 @@ func (Policy) Header(ns string) HeaderRow {
|
|||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Policy) Render(o interface{}, gvr string, r *Row) error {
|
||||
panic("NYI")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (PersistentVolume) Header(string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (PersistentVolume) Render(o interface{}, ns string, r *Row) error {
|
||||
func (p PersistentVolume) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected PersistentVolume, but got %T", o)
|
||||
|
|
@ -79,8 +79,8 @@ func (PersistentVolume) Render(o interface{}, ns string, r *Row) error {
|
|||
|
||||
size := pv.Spec.Capacity[v1.ResourceStorage]
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
fields = append(fields,
|
||||
r.ID = MetaFQN(pv.ObjectMeta)
|
||||
r.Fields = Fields{
|
||||
pv.Name,
|
||||
size.String(),
|
||||
accessMode(pv.Spec.AccessModes),
|
||||
|
|
@ -90,8 +90,7 @@ func (PersistentVolume) Render(o interface{}, ns string, r *Row) error {
|
|||
class,
|
||||
pv.Status.Reason,
|
||||
toAge(pv.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(pv.ObjectMeta), fields
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func (PersistentVolumeClaim) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (PersistentVolumeClaim) Render(o interface{}, ns string, r *Row) error {
|
||||
func (p PersistentVolumeClaim) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected PersistentVolumeClaim, but got %T", o)
|
||||
|
|
@ -84,11 +84,12 @@ func (PersistentVolumeClaim) Render(o interface{}, ns string, r *Row) error {
|
|||
}
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(pvc.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(p.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, pvc.Namespace)
|
||||
r.Fields = append(r.Fields, pvc.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
pvc.Name,
|
||||
string(phase),
|
||||
pvc.Spec.VolumeName,
|
||||
|
|
@ -97,7 +98,6 @@ func (PersistentVolumeClaim) Render(o interface{}, ns string, r *Row) error {
|
|||
class,
|
||||
toAge(pvc.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(pvc.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ type Header struct {
|
|||
// HeaderRow represents a table header.
|
||||
type HeaderRow []Header
|
||||
|
||||
// Columns return header row columns as strings.
|
||||
func (h HeaderRow) Columns() []string {
|
||||
cc := make([]string, len(h))
|
||||
for i, c := range h {
|
||||
cc[i] = c.Name
|
||||
}
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// HasAge returns true if table has an age column.
|
||||
func (h HeaderRow) HasAge() bool {
|
||||
for _, r := range h {
|
||||
|
|
|
|||
|
|
@ -58,10 +58,11 @@ func TestRowDelete(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
rows := u.rows.Delete(u.id)
|
||||
assert.Equal(t, u.e, rows)
|
||||
rows := uc.rows.Delete(uc.id)
|
||||
assert.Equal(t, uc.e, rows)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -135,10 +136,11 @@ func TestSortText(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
u.rows.Sort(u.col, u.asc)
|
||||
assert.Equal(t, u.e, u.rows)
|
||||
uc.rows.Sort(uc.col, uc.asc)
|
||||
assert.Equal(t, uc.e, uc.rows)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -175,10 +177,11 @@ func TestSortDuration(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
u.rows.Sort(u.col, u.asc)
|
||||
assert.Equal(t, u.e, u.rows)
|
||||
uc.rows.Sort(uc.col, uc.asc)
|
||||
assert.Equal(t, uc.e, uc.rows)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -216,10 +219,11 @@ func TestSortMetrics(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
uc := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
u.rows.Sort(u.col, u.asc)
|
||||
assert.Equal(t, u.e, u.rows)
|
||||
uc.rows.Sort(uc.col, uc.asc)
|
||||
assert.Equal(t, uc.e, uc.rows)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (ReplicaSet) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (ReplicaSet) Render(o interface{}, ns string, r *Row) error {
|
||||
func (s ReplicaSet) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected ReplicaSet, but got %T", o)
|
||||
|
|
@ -64,18 +64,18 @@ func (ReplicaSet) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(rs.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(s.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, rs.Namespace)
|
||||
r.Fields = append(r.Fields, rs.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
rs.Name,
|
||||
strconv.Itoa(int(*rs.Spec.Replicas)),
|
||||
strconv.Itoa(int(rs.Status.Replicas)),
|
||||
strconv.Itoa(int(rs.Status.ReadyReplicas)),
|
||||
toAge(rs.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
r.ID, r.Fields = MetaFQN(rs.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,28 +32,27 @@ func (ServiceAccount) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (ServiceAccount) Render(o interface{}, ns string, r *Row) error {
|
||||
func (s ServiceAccount) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected ServiceAccount, but got %T", o)
|
||||
}
|
||||
var s v1.ServiceAccount
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &s)
|
||||
var sa v1.ServiceAccount
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &sa)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(sa.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(s.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, s.Namespace)
|
||||
r.Fields = append(r.Fields, sa.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
s.Name,
|
||||
strconv.Itoa(len(s.Secrets)),
|
||||
toAge(s.ObjectMeta.CreationTimestamp),
|
||||
r.Fields = append(r.Fields,
|
||||
sa.Name,
|
||||
strconv.Itoa(len(sa.Secrets)),
|
||||
toAge(sa.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
|
||||
r.ID, r.Fields = MetaFQN(s.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,29 +34,28 @@ func (Secret) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Secret) Render(o interface{}, ns string, r *Row) error {
|
||||
func (s Secret) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected Secret, but got %T", o)
|
||||
}
|
||||
var s v1.Secret
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &s)
|
||||
var sec v1.Secret
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &sec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(sec.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(s.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, s.Namespace)
|
||||
r.Fields = append(r.Fields, sec.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
s.Name,
|
||||
string(s.Type),
|
||||
strconv.Itoa(len(s.Data)),
|
||||
toAge(s.ObjectMeta.CreationTimestamp),
|
||||
r.Fields = append(r.Fields,
|
||||
sec.Name,
|
||||
string(sec.Type),
|
||||
strconv.Itoa(len(sec.Data)),
|
||||
toAge(sec.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
|
||||
r.ID, r.Fields = MetaFQN(s.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,5 @@ func (Subject) Header(ns string) HeaderRow {
|
|||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Subject) Render(o interface{}, gvr string, r *Row) error {
|
||||
panic("NYI")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func (Service) Header(ns string) HeaderRow {
|
|||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (Service) Render(o interface{}, ns string, r *Row) error {
|
||||
func (s Service) Render(o interface{}, ns string, r *Row) error {
|
||||
raw, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected Service, but got %T", o)
|
||||
|
|
@ -49,11 +49,12 @@ func (Service) Render(o interface{}, ns string, r *Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := make(Fields, 0, len(r.Fields))
|
||||
r.ID = MetaFQN(svc.ObjectMeta)
|
||||
r.Fields = make(Fields, 0, len(s.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
fields = append(fields, svc.Namespace)
|
||||
r.Fields = append(r.Fields, svc.Namespace)
|
||||
}
|
||||
fields = append(fields,
|
||||
r.Fields = append(r.Fields,
|
||||
svc.ObjectMeta.Name,
|
||||
string(svc.Spec.Type),
|
||||
svc.Spec.ClusterIP,
|
||||
|
|
@ -63,8 +64,6 @@ func (Service) Render(o interface{}, ns string, r *Row) error {
|
|||
toAge(svc.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
|
||||
r.ID, r.Fields = MetaFQN(svc.ObjectMeta), fields
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package ui_test
|
||||
|
||||
// BOZO!!
|
||||
// import (
|
||||
// "testing"
|
||||
|
||||
// "github.com/derailed/k9s/internal/render"
|
||||
// "github.com/derailed/k9s/internal/ui"
|
||||
// "github.com/gdamore/tcell"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
|
||||
// func TestDefaultColorer(t *testing.T) {
|
||||
// uu := map[string]struct {
|
||||
// re render.RowEvent
|
||||
// e tcell.Color
|
||||
// }{
|
||||
// "default": {render.RowEvent{}, ui.StdColor},
|
||||
// "add": {render.RowEvent{Kind: render.EventAdd}, ui.AddColor},
|
||||
// "delete": {render.RowEvent{Kind: render.EventDelete}, ui.KillColor},
|
||||
// "update": {render.RowEvent{Kind: render.EventUpdate}, ui.ModColor},
|
||||
// }
|
||||
|
||||
// for k := range uu {
|
||||
// u := uu[k]
|
||||
// t.Run(k, func(t *testing.T) {
|
||||
// assert.Equal(t, u.e, ui.DefaultColorer("", u.re))
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
package ui
|
||||
|
||||
// BOZO!!
|
||||
// func TestGroupSort(t *testing.T) {
|
||||
// uu := []struct {
|
||||
// asc bool
|
||||
// rows []string
|
||||
// expect []string
|
||||
// }{
|
||||
// {true, []string{"200m", "100m"}, []string{"100m", "200m"}},
|
||||
// {true, []string{"200m", "100m"}, []string{"100m", "200m"}},
|
||||
// {false, []string{"200m", "100m"}, []string{"200m", "100m"}},
|
||||
// {true, []string{"10", "1"}, []string{"1", "10"}},
|
||||
// {false, []string{"10", "1"}, []string{"10", "1"}},
|
||||
// {true, []string{"100Mi", "10Mi"}, []string{"10Mi", "100Mi"}},
|
||||
// {false, []string{"100Mi", "10Mi"}, []string{"100Mi", "10Mi"}},
|
||||
// {true, []string{"xyz", "abc"}, []string{"abc", "xyz"}},
|
||||
// {false, []string{"xyz", "abc"}, []string{"xyz", "abc"}},
|
||||
// {true, []string{"2m30s", "1m10s"}, []string{"1m10s", "2m30s"}},
|
||||
// {true, []string{"3d", "1d"}, []string{"1d", "3d"}},
|
||||
|
||||
// {true, []string{"95h", "93h"}, []string{"93h", "95h"}},
|
||||
// {true, []string{"95d", "93d"}, []string{"93d", "95d"}},
|
||||
// {true, []string{"1h10m", "59m"}, []string{"59m", "1h10m"}},
|
||||
// {true, []string{"95m", "1h30m"}, []string{"1h30m", "95m"}},
|
||||
// {true, []string{"b-21", "b-2"}, []string{"b-2", "b-21"}},
|
||||
// {false, []string{"b-21", "b-2"}, []string{"b-21", "b-2"}},
|
||||
// {true, []string{"4m", "3m2s"}, []string{"3m2s", "4m"}},
|
||||
// {true, []string{"3y37d", "2y4d"}, []string{"2y4d", "3y37d"}},
|
||||
// }
|
||||
|
||||
// for _, u := range uu {
|
||||
// g := GroupSorter{rows: u.rows, asc: u.asc}
|
||||
// sort.Sort(g)
|
||||
// assert.Equal(t, u.expect, g.rows)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRowSort(t *testing.T) {
|
||||
// uu := []struct {
|
||||
// asc bool
|
||||
// rows, expect resource.Rows
|
||||
// }{
|
||||
// {
|
||||
// true,
|
||||
// resource.Rows{resource.Row{"200m"}, resource.Row{"100m"}},
|
||||
// resource.Rows{resource.Row{"100m"}, resource.Row{"200m"}},
|
||||
// },
|
||||
// {
|
||||
// false,
|
||||
// resource.Rows{resource.Row{"200m"}, resource.Row{"100m"}},
|
||||
// resource.Rows{resource.Row{"200m"}, resource.Row{"100m"}},
|
||||
// },
|
||||
// {
|
||||
// true,
|
||||
// resource.Rows{resource.Row{"200Mi"}, resource.Row{"100Mi"}},
|
||||
// resource.Rows{resource.Row{"100Mi"}, resource.Row{"200Mi"}},
|
||||
// },
|
||||
// {
|
||||
// false,
|
||||
// resource.Rows{resource.Row{"200Mi"}, resource.Row{"100Mi"}},
|
||||
// resource.Rows{resource.Row{"200Mi"}, resource.Row{"100Mi"}},
|
||||
// },
|
||||
// {
|
||||
// true,
|
||||
// resource.Rows{resource.Row{"8m4s"}, resource.Row{"31m"}},
|
||||
// resource.Rows{resource.Row{"8m4s"}, resource.Row{"31m"}},
|
||||
// },
|
||||
// {
|
||||
// true,
|
||||
// resource.Rows{resource.Row{"n/a"}, resource.Row{"31m"}},
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// },
|
||||
// {
|
||||
// true,
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// },
|
||||
// {
|
||||
// false,
|
||||
// resource.Rows{resource.Row{"n/a"}, resource.Row{"31m"}},
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// },
|
||||
// {
|
||||
// false,
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// resource.Rows{resource.Row{"31m"}, resource.Row{"n/a"}},
|
||||
// },
|
||||
// }
|
||||
|
||||
// for _, u := range uu {
|
||||
// r := RowSorter{index: 0, rows: u.rows, asc: u.asc}
|
||||
// sort.Sort(r)
|
||||
// assert.Equal(t, u.expect, r.rows)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestIsDurationSort(t *testing.T) {
|
||||
// uu := map[string]struct {
|
||||
// s1, s2 string
|
||||
// asc, e bool
|
||||
// }{
|
||||
// "ascLess": {"10h5m", "2h10m", true, false},
|
||||
// "descGreater": {"10h5m", "2h10m", false, true},
|
||||
// "ascEqual": {"2h10m", "2h10m", true, true},
|
||||
// "descEqual": {"2h10m", "2h10m", false, true},
|
||||
// "ascGreater": {"10h10m", "2h5m", true, false},
|
||||
// }
|
||||
|
||||
// for k := range uu {
|
||||
// u := uu[k]
|
||||
// t.Run(k, func(t *testing.T) {
|
||||
// less, ok := isDurationSort(u.asc, u.s1, u.s2)
|
||||
// assert.True(t, ok)
|
||||
// assert.Equal(t, u.e, less)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
|
@ -33,7 +33,6 @@ type Table struct {
|
|||
cmdBuff *CmdBuff
|
||||
styles *config.Styles
|
||||
sortCol SortColumn
|
||||
sortFn SortFn
|
||||
colorerFn render.ColorerFunc
|
||||
decorateFn DecorateFunc
|
||||
}
|
||||
|
|
@ -158,7 +157,6 @@ func (t *Table) doUpdate(data render.TableData) {
|
|||
|
||||
t.adjustSorter(data)
|
||||
|
||||
var row int
|
||||
fg := config.AsColor(t.styles.GetTable().Header.FgColor)
|
||||
bg := config.AsColor(t.styles.GetTable().Header.BgColor)
|
||||
for col, h := range data.Header {
|
||||
|
|
@ -167,8 +165,6 @@ func (t *Table) doUpdate(data render.TableData) {
|
|||
c.SetBackgroundColor(bg)
|
||||
c.SetTextColor(fg)
|
||||
}
|
||||
row++
|
||||
|
||||
data.RowEvents.Sort(data.Namespace, t.sortCol.index, t.sortCol.asc)
|
||||
|
||||
pads := make(MaxyPad, len(data.Header))
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
cpuRX = regexp.MustCompile(`\A.{0,1}CPU`)
|
||||
memRX = regexp.MustCompile(`\A.{0,1}MEM`)
|
||||
|
||||
// LabelCmd identifies a label query
|
||||
LabelCmd = regexp.MustCompile(`\A\-l`)
|
||||
|
||||
|
|
@ -88,46 +85,6 @@ func SkinTitle(fmat string, style config.Frame) string {
|
|||
return fmat
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func sortRows(evts resource.RowEvents, sortFn SortFn, sortCol SortColumn, keys []string) {
|
||||
// rows := make(resource.Rows, 0, len(evts))
|
||||
// for k, r := range evts {
|
||||
// rows = append(rows, append(r.Fields, k))
|
||||
// }
|
||||
// sortFn(rows, sortCol)
|
||||
|
||||
// for i, r := range rows {
|
||||
// keys[i] = r[len(r)-1]
|
||||
// }
|
||||
// }
|
||||
|
||||
// func defaultSort(rows resource.Rows, sortCol SortColumn) {
|
||||
// t := RowSorter{rows: rows, index: sortCol.index, asc: sortCol.asc}
|
||||
// sort.Sort(t)
|
||||
// }
|
||||
|
||||
// BOZO!!
|
||||
// func sortAllRows(col SortColumn, rows resource.RowEvents, sortFn SortFn) (resource.Row, map[string]resource.Row) {
|
||||
// keys := make([]string, len(rows))
|
||||
// sortRows(rows, sortFn, col, keys)
|
||||
|
||||
// sec := make(map[string]resource.Row, len(rows))
|
||||
// for _, k := range keys {
|
||||
// grp := rows[k].Fields[col.index]
|
||||
// sec[grp] = append(sec[grp], k)
|
||||
// }
|
||||
|
||||
// // Performs secondary to sort by name for each groups.
|
||||
// prim := make(resource.Row, 0, len(sec))
|
||||
// for k, v := range sec {
|
||||
// sort.Strings(v)
|
||||
// prim = append(prim, k)
|
||||
// }
|
||||
// sort.Sort(GroupSorter{prim, col.asc})
|
||||
|
||||
// return prim, sec
|
||||
// }
|
||||
|
||||
func sortIndicator(col SortColumn, style config.Table, index int, name string) string {
|
||||
if col.index != index {
|
||||
return name
|
||||
|
|
@ -170,10 +127,9 @@ func rxFilter(q string, data render.TableData) (render.TableData, error) {
|
|||
}
|
||||
|
||||
func fuzzyFilter(q string, index int, data render.TableData) render.TableData {
|
||||
var ss, kk []string
|
||||
var ss []string
|
||||
for _, re := range data.RowEvents {
|
||||
ss = append(ss, re.Row.Fields[index])
|
||||
kk = append(kk, re.Row.ID)
|
||||
}
|
||||
|
||||
filtered := render.TableData{
|
||||
|
|
|
|||
|
|
@ -40,76 +40,3 @@ func TestTrimLabelSelector(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func TestTVSortRows(t *testing.T) {
|
||||
// uu := []struct {
|
||||
// rows resource.RowEvents
|
||||
// col int
|
||||
// asc bool
|
||||
// first resource.Row
|
||||
// e []string
|
||||
// }{
|
||||
// {
|
||||
// resource.RowEvents{
|
||||
// "row1": {Fields: resource.Row{"x", "y"}},
|
||||
// "row2": {Fields: resource.Row{"a", "b"}},
|
||||
// },
|
||||
// 0,
|
||||
// true,
|
||||
// resource.Row{"a", "b"},
|
||||
// []string{"row2", "row1"},
|
||||
// },
|
||||
// {
|
||||
// resource.RowEvents{
|
||||
// "row1": {Fields: resource.Row{"x", "y"}},
|
||||
// "row2": {Fields: resource.Row{"a", "b"}},
|
||||
// },
|
||||
// 1,
|
||||
// true,
|
||||
// resource.Row{"a", "b"},
|
||||
// []string{"row2", "row1"},
|
||||
// },
|
||||
// {
|
||||
// resource.RowEvents{
|
||||
// "row1": {Fields: resource.Row{"x", "y"}},
|
||||
// "row2": {Fields: resource.Row{"a", "b"}},
|
||||
// },
|
||||
// 1,
|
||||
// false,
|
||||
// resource.Row{"x", "y"},
|
||||
// []string{"row1", "row2"},
|
||||
// },
|
||||
// {
|
||||
// resource.RowEvents{
|
||||
// "row1": {Fields: resource.Row{"2175h48m0.06015s", "y"}},
|
||||
// "row2": {Fields: resource.Row{"403h42m34.060166s", "b"}},
|
||||
// },
|
||||
// 0,
|
||||
// true,
|
||||
// resource.Row{"403h42m34.060166s", "b"},
|
||||
// []string{"row2", "row1"},
|
||||
// },
|
||||
// }
|
||||
|
||||
// for _, u := range uu {
|
||||
// keys := make([]string, len(u.rows))
|
||||
// sortRows(u.rows, defaultSort, SortColumn{index: u.col, colCount: len(u.rows), asc: u.asc}, keys)
|
||||
// assert.Equal(t, u.e, keys)
|
||||
// assert.Equal(t, u.first, u.rows[u.e[0]].Fields)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func BenchmarkTableSortRows(b *testing.B) {
|
||||
// evts := resource.RowEvents{
|
||||
// "row1": {Fields: resource.Row{"x", "y"}},
|
||||
// "row2": {Fields: resource.Row{"a", "b"}},
|
||||
// }
|
||||
// sc := SortColumn{index: 0, colCount: 2, asc: true}
|
||||
// keys := make([]string, len(evts))
|
||||
// b.ResetTimer()
|
||||
// b.ReportAllocs()
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// sortRows(evts, defaultSort, sc, keys)
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
)
|
||||
import "github.com/derailed/k9s/internal/render"
|
||||
|
||||
type (
|
||||
// SortFn represent a function that can sort columnar data.
|
||||
|
|
@ -12,10 +12,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
aliasTitle = "Aliases"
|
||||
aliasTitleFmt = " [mediumseagreen::b]%s([fuchsia::b]%d[fuchsia::-][mediumseagreen::-]) "
|
||||
)
|
||||
const aliasTitle = "Aliases"
|
||||
|
||||
// Alias represents a command alias view.
|
||||
type Alias struct {
|
||||
|
|
@ -32,7 +29,6 @@ func NewAlias(gvr client.GVR) ResourceViewer {
|
|||
a.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorMediumSpringGreen, tcell.AttrNone)
|
||||
a.SetBindKeysFn(a.bindKeys)
|
||||
a.SetContextFn(a.aliasContext)
|
||||
// a.GetTable().SetEnterFn(a.gotoCmd)
|
||||
|
||||
return &a
|
||||
}
|
||||
|
|
@ -45,12 +41,9 @@ func (a *Alias) bindKeys(aa ui.KeyActions) {
|
|||
aa.Delete(ui.KeyShiftA, ui.KeyShiftN, tcell.KeyCtrlS, tcell.KeyCtrlSpace, ui.KeySpace)
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyEnter: ui.NewKeyAction("Goto", a.gotoCmd, true),
|
||||
// BOZO!!
|
||||
// tcell.KeyEscape: ui.NewKeyAction("Reset", a.resetCmd, false),
|
||||
// ui.KeySlash: ui.NewKeyAction("Filter", a.GetTable().activateCmd, false),
|
||||
ui.KeyShiftR: ui.NewKeyAction("Sort Resource", a.GetTable().SortColCmd(0, true), false),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort Command", a.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftA: ui.NewKeyAction("Sort ApiGroup", a.GetTable().SortColCmd(2, true), false),
|
||||
ui.KeyShiftR: ui.NewKeyAction("Sort Resource", a.GetTable().SortColCmd(0, true), false),
|
||||
ui.KeyShiftC: ui.NewKeyAction("Sort Command", a.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftA: ui.NewKeyAction("Sort ApiGroup", a.GetTable().SortColCmd(2, true), false),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +53,9 @@ func (a *Alias) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if r != 0 {
|
||||
s := ui.TrimCell(a.GetTable().SelectTable, r, 1)
|
||||
tokens := strings.Split(s, ",")
|
||||
a.App().gotoResource(tokens[0])
|
||||
if err := a.App().gotoResource(tokens[0]); err != nil {
|
||||
a.App().Flash().Err(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -69,71 +64,3 @@ func (a *Alias) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *Alias) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !a.GetTable().SearchBuff().Empty() {
|
||||
a.GetTable().SearchBuff().Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
return a.App().PrevCmd(evt)
|
||||
}
|
||||
|
||||
func (a *Alias) gotoCmd1(app *App, ns, res, path string) {
|
||||
log.Debug().Msgf("GOTO %q -- %q -- %q", ns, res, path)
|
||||
app.gotoResource(client.GVR(path).ToR())
|
||||
// r, _ := a.GetTable().GetSelection()
|
||||
// if r != 0 {
|
||||
// s := ui.TrimCell(a.GetTable().SelectTable, r, 1)
|
||||
// tokens := strings.Split(s, ",")
|
||||
// a.App().Content.Pop()
|
||||
// if err := a.App().gotoResource(tokens[0]); err != nil {
|
||||
// a.App().Flash().Err(err)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// if a.GetTable().SearchBuff().IsActive() {
|
||||
// return a.GetTable().activateCmd(evt)
|
||||
// }
|
||||
|
||||
// return evt
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func (a *Alias) hydrate() render.TableData {
|
||||
// var re render.Alias
|
||||
|
||||
// data := render.TableData{
|
||||
// Header: re.Header(render.AllNamespaces),
|
||||
// RowEvents: make(render.RowEvents, 0, len(aliases.Alias)),
|
||||
// Namespace: resource.NotNamespaced,
|
||||
// }
|
||||
|
||||
// aa := make(config.ShortNames, len(aliases.Alias))
|
||||
// for alias, gvr := range aliases.Alias {
|
||||
// if _, ok := aa[gvr]; ok {
|
||||
// aa[gvr] = append(aa[gvr], alias)
|
||||
// } else {
|
||||
// aa[gvr] = []string{alias}
|
||||
// }
|
||||
// }
|
||||
|
||||
// for gvr, aliases := range aa {
|
||||
// var row render.Row
|
||||
// if err := re.Render(aliases, gvr, &row); err != nil {
|
||||
// log.Error().Err(err).Msgf("Alias render failed")
|
||||
// continue
|
||||
// }
|
||||
// data.RowEvents = append(data.RowEvents, render.RowEvent{
|
||||
// Kind: render.EventAdd,
|
||||
// Row: row,
|
||||
// })
|
||||
// }
|
||||
|
||||
// return data
|
||||
// }
|
||||
|
||||
// func (a *Alias) resetTitle() {
|
||||
// a.SetTitle(fmt.Sprintf(aliasTitleFmt, aliasTitle, a.GetRowCount()-1))
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -211,20 +211,26 @@ func (a *App) refreshIndicator() {
|
|||
cluster := model.NewCluster(a.Conn(), mx)
|
||||
var cmx client.ClusterMetrics
|
||||
nos, nmx, err := fetchResources(a)
|
||||
cpu, mem := "0", "0"
|
||||
if err == nil {
|
||||
cluster.Metrics(nos, nmx, &cmx)
|
||||
cpu = render.AsPerc(cmx.PercCPU)
|
||||
if cpu == "0" {
|
||||
cpu = render.NAValue
|
||||
}
|
||||
mem = render.AsPerc(cmx.PercMEM)
|
||||
if mem == "0" {
|
||||
mem = render.NAValue
|
||||
}
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("unable to refresh cluster indicator")
|
||||
return
|
||||
}
|
||||
|
||||
info := fmt.Sprintf(
|
||||
if err := cluster.Metrics(nos, nmx, &cmx); err != nil {
|
||||
log.Error().Err(err).Msgf("unable to refresh cluster indicator")
|
||||
return
|
||||
}
|
||||
|
||||
cpu := render.AsPerc(cmx.PercCPU)
|
||||
if cpu == "0" {
|
||||
cpu = render.NAValue
|
||||
}
|
||||
mem := render.AsPerc(cmx.PercMEM)
|
||||
if mem == "0" {
|
||||
mem = render.NAValue
|
||||
}
|
||||
|
||||
a.indicator().SetPermanent(fmt.Sprintf(
|
||||
indicatorFmt,
|
||||
a.version,
|
||||
cluster.ClusterName(),
|
||||
|
|
@ -232,8 +238,7 @@ func (a *App) refreshIndicator() {
|
|||
cluster.Version(),
|
||||
cpu,
|
||||
mem,
|
||||
)
|
||||
a.indicator().SetPermanent(info)
|
||||
))
|
||||
}
|
||||
|
||||
func (a *App) switchNS(ns string) bool {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ package view
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
|
|
@ -18,10 +16,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
benchTitle = "Benchmarks"
|
||||
resultTitle = "Benchmark Results"
|
||||
)
|
||||
const resultTitle = "Benchmark Results"
|
||||
|
||||
// Benchmark represents a service benchmark results view.
|
||||
type Benchmark struct {
|
||||
|
|
@ -50,31 +45,19 @@ func (b *Benchmark) benchContext(ctx context.Context) context.Context {
|
|||
return context.WithValue(ctx, internal.KeyDir, benchDir(b.App().Config))
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// // Start runs the refresh loop
|
||||
// func (b *Bench) Start() {
|
||||
// log.Debug().Msgf(">>>> Bench START")
|
||||
// var ctx context.Context
|
||||
|
||||
// ctx, b.cancelFn = context.WithCancel(context.Background())
|
||||
// if err := b.watchBenchDir(ctx); err != nil {
|
||||
// b.App().Flash().Errf("Unable to watch benchmarks directory %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
func (b *Benchmark) viewBench(app *App, ns, res, path string) {
|
||||
log.Debug().Msgf("VIEWBENCH %q -- %q -- %q", ns, res, path)
|
||||
data, err := readBenchFile(app.Config, b.benchFile())
|
||||
if err != nil {
|
||||
b.App().Flash().Errf("Unable to load bench file %s", err)
|
||||
app.Flash().Errf("Unable to load bench file %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
b.details.SetText(data)
|
||||
b.details.SetSubject(fileToSubject(path))
|
||||
b.App().inject(b.details)
|
||||
|
||||
return
|
||||
if err := app.inject(b.details); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fileToSubject(path string) string {
|
||||
|
|
@ -84,60 +67,30 @@ func fileToSubject(path string) string {
|
|||
return ee[0] + "/" + ee[1]
|
||||
}
|
||||
|
||||
func (b *Benchmark) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !b.GetTable().RowSelected() {
|
||||
return nil
|
||||
}
|
||||
// BOZO!!
|
||||
// func (b *Benchmark) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
// if !b.GetTable().RowSelected() {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
sel, file := b.GetTable().GetSelectedItem(), b.benchFile()
|
||||
dir := filepath.Join(perf.K9sBenchDir, b.App().Config.K9s.CurrentCluster)
|
||||
showModal(b.App().Content.Pages, fmt.Sprintf("Delete benchmark `%s?", file), func() {
|
||||
if err := os.Remove(filepath.Join(dir, file)); err != nil {
|
||||
b.App().Flash().Errf("Unable to delete file %s", err)
|
||||
return
|
||||
}
|
||||
b.App().Flash().Infof("Benchmark %s deleted!", sel)
|
||||
})
|
||||
// sel, file := b.GetTable().GetSelectedItem(), b.benchFile()
|
||||
// dir := filepath.Join(perf.K9sBenchDir, b.App().Config.K9s.CurrentCluster)
|
||||
// showModal(b.App().Content.Pages, fmt.Sprintf("Delete benchmark `%s?", file), func() {
|
||||
// if err := os.Remove(filepath.Join(dir, file)); err != nil {
|
||||
// b.App().Flash().Errf("Unable to delete file %s", err)
|
||||
// return
|
||||
// }
|
||||
// b.App().Flash().Infof("Benchmark %s deleted!", sel)
|
||||
// })
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (b *Benchmark) benchFile() string {
|
||||
r := b.GetTable().GetSelectedRowIndex()
|
||||
return ui.TrimCell(b.GetTable().SelectTable, r, 7)
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func (b *Benchmark) watchBenchDir(ctx context.Context) error {
|
||||
// w, err := fsnotify.NewWatcher()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// go func() {
|
||||
// for {
|
||||
// select {
|
||||
// case evt := <-w.Events:
|
||||
// log.Debug().Msgf("Bench event %#v", evt)
|
||||
// b.App().QueueUpdateDraw(func() {
|
||||
// b.Refresh()
|
||||
// })
|
||||
// case err := <-w.Errors:
|
||||
// log.Info().Err(err).Msg("Dir Watcher failed")
|
||||
// return
|
||||
// case <-ctx.Done():
|
||||
// log.Debug().Msg("!!!! FS WATCHER DONE!!")
|
||||
// if err := w.Close(); err != nil {
|
||||
// log.Error().Err(err).Msg("Closing bench watched")
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
|
||||
// return w.Add(benchDir(b.App().Config))
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
|
|
@ -145,10 +98,6 @@ func benchDir(cfg *config.Config) string {
|
|||
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster)
|
||||
}
|
||||
|
||||
func loadBenchDir(cfg *config.Config) ([]os.FileInfo, error) {
|
||||
return ioutil.ReadDir(benchDir(cfg))
|
||||
}
|
||||
|
||||
func readBenchFile(cfg *config.Config, n string) (string, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(benchDir(cfg), n))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := b.Table.Init(ctx); err != nil {
|
||||
if err = b.Table.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if !dao.IsK9sMeta(b.meta) {
|
||||
|
|
@ -79,7 +79,6 @@ func (b *Browser) Init(ctx context.Context) error {
|
|||
log.Debug().Msgf("ACCESSOR FOR %s -- %#v", b.gvr, b.accessor)
|
||||
|
||||
b.envFn = b.defaultK9sEnv
|
||||
b.Table.setFilterFn(b.filterBrowser)
|
||||
b.setNamespace(b.App().Config.ActiveNamespace())
|
||||
b.refresh()
|
||||
row, _ := b.GetSelection()
|
||||
|
|
@ -111,10 +110,7 @@ func (b *Browser) Stop() {
|
|||
}
|
||||
|
||||
func (b *Browser) Refresh() {
|
||||
// BOZO!!
|
||||
// b.app.QueueUpdateDraw(func() {
|
||||
b.refresh()
|
||||
// })
|
||||
}
|
||||
|
||||
// Name returns the component name.
|
||||
|
|
@ -142,12 +138,6 @@ func (b *Browser) GetTable() *Table {
|
|||
return b.Table
|
||||
}
|
||||
|
||||
func (b *Browser) filterBrowser(sel string) {
|
||||
panic("NYI")
|
||||
// b.list.SetLabelSelector(sel)
|
||||
b.refresh()
|
||||
}
|
||||
|
||||
func (b *Browser) update(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
|
|
@ -263,7 +253,7 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *Browser) defaultEnter(app *App, ns, _, sel string) {
|
||||
func (b *Browser) defaultEnter(app *App, _, _, sel string) {
|
||||
log.Debug().Msgf("--------- Resource %q Verbs %v", sel, b.meta.Verbs)
|
||||
ns, n := client.Namespaced(sel)
|
||||
yaml, err := dao.Describe(b.app.Conn(), b.gvr, ns, n)
|
||||
|
|
@ -277,7 +267,6 @@ func (b *Browser) defaultEnter(app *App, ns, _, sel string) {
|
|||
details.SetTextColor(b.app.Styles.FgColor())
|
||||
details.SetText(colorizeYAML(b.app.Styles.Views().Yaml, yaml))
|
||||
details.ScrollToBeginning()
|
||||
|
||||
if err := b.app.inject(details); err != nil {
|
||||
b.app.Flash().Err(err)
|
||||
}
|
||||
|
|
@ -317,7 +306,9 @@ func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
details.SetTextColor(b.app.Styles.FgColor())
|
||||
details.SetText(colorizeYAML(b.app.Styles.Views().Yaml, raw))
|
||||
details.ScrollToBeginning()
|
||||
b.app.inject(details)
|
||||
if err := b.app.inject(details); err != nil {
|
||||
b.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,9 @@ func (v *clusterInfoView) refreshMetrics(cluster *model.Cluster, row int) {
|
|||
}
|
||||
|
||||
var cmx client.ClusterMetrics
|
||||
cluster.Metrics(nos, nmx, &cmx)
|
||||
if err := cluster.Metrics(nos, nmx, &cmx); err != nil {
|
||||
log.Error().Err(err).Msgf("failed to retrieve cluster metrics")
|
||||
}
|
||||
c := v.GetCell(row, 1)
|
||||
cpu := render.AsPerc(cmx.PercCPU)
|
||||
if cpu == "0" {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ func (c *command) exec(gvr string, comp model.Component) error {
|
|||
log.Error().Err(err).Msg("Config save failed!")
|
||||
}
|
||||
c.app.Content.Stack.ClearHistory()
|
||||
return c.app.inject(comp)
|
||||
|
||||
return nil
|
||||
return c.app.inject(comp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ func (c *CronJob) showJobs(app *App, ns, res, path string) {
|
|||
|
||||
v := NewJob(client.GVR("batch/v1/jobs"))
|
||||
v.SetContextFn(jobCtx(path, string(cj.UID)))
|
||||
app.inject(v)
|
||||
if err := app.inject(v); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func jobCtx(path, uid string) ContextFunc {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func NewHelp() *Help {
|
|||
}
|
||||
|
||||
// Init initializes the component.
|
||||
func (v *Help) Init(ctx context.Context) (err error) {
|
||||
func (v *Help) Init(ctx context.Context) error {
|
||||
if err := v.Table.Init(ctx); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,27 @@
|
|||
package view_test
|
||||
|
||||
// import (
|
||||
// "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// "github.com/derailed/k9s/internal/dao"
|
||||
// "github.com/derailed/k9s/internal/ui"
|
||||
// "github.com/derailed/k9s/internal/view"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/view"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// BOZO!!
|
||||
// func TestHelpNew(t *testing.T) {
|
||||
// ctx := makeCtx()
|
||||
func TestHelp(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
|
||||
// app := ctx.Value(ui.KeyApp).(*view.App)
|
||||
// po := view.NewPod(client.GVR("v1/pods"))
|
||||
// po.Init(ctx)
|
||||
// app.Content.Push(po)
|
||||
app := ctx.Value(ui.KeyApp).(*view.App)
|
||||
po := view.NewPod(client.GVR("v1/pods"))
|
||||
po.Init(ctx)
|
||||
app.Content.Push(po)
|
||||
|
||||
// v := view.NewHelp()
|
||||
// v.Init(ctx)
|
||||
v := view.NewHelp()
|
||||
|
||||
// assert.Equal(t, 32, v.GetRowCount())
|
||||
// assert.Equal(t, 10, v.GetColumnCount())
|
||||
// assert.Equal(t, "<backspace>", v.GetCell(1, 0).Text)
|
||||
// assert.Equal(t, "Erase", v.GetCell(1, 1).Text)
|
||||
// }
|
||||
assert.Nil(t, v.Init(ctx))
|
||||
assert.Equal(t, 25, v.GetRowCount())
|
||||
assert.Equal(t, 10, v.GetColumnCount())
|
||||
assert.Equal(t, "<backspace>", v.GetCell(1, 0).Text)
|
||||
assert.Equal(t, "Erase", v.GetCell(1, 1).Text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func showPodsWithLabels(app *App, path string, sel map[string]string) {
|
||||
|
|
@ -38,7 +36,9 @@ func showPods(app *App, path, labelSel, fieldSel string) {
|
|||
if err := app.Config.SetActiveNamespace(ns); err != nil {
|
||||
log.Error().Err(err).Msg("Config NS set failed!")
|
||||
}
|
||||
app.inject(v)
|
||||
if err := app.inject(v); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func podCtx(path, labelSel, fieldSel string) ContextFunc {
|
||||
|
|
@ -118,9 +118,3 @@ func fqn(ns, n string) string {
|
|||
}
|
||||
return ns + "/" + n
|
||||
}
|
||||
|
||||
// AsNumb prints a number with thousand separator.
|
||||
func asNum(n int) string {
|
||||
p := message.NewPrinter(language.English)
|
||||
return p.Sprintf("%d", n)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func NewJob(gvr client.GVR) ResourceViewer {
|
|||
return &j
|
||||
}
|
||||
|
||||
// BOZO!! Change enter signature?
|
||||
// TODO!! Change enter signature?
|
||||
func (*Job) showPods(app *App, _, res, path string) {
|
||||
o, err := app.factory.Get("batch/v1/jobs", path, labels.Everything())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -55,5 +55,7 @@ func (l *LogsExtender) showLogs(path string, prev bool) {
|
|||
log.Debug().Msgf("CUSTOM CO FUNC")
|
||||
co = l.containerFn()
|
||||
}
|
||||
l.App().inject(NewLog(client.GVR(l.GVR()), path, co, prev))
|
||||
if err := l.App().inject(NewLog(client.GVR(l.GVR()), path, co, prev)); err != nil {
|
||||
l.App().Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const nodeTitle = "Nodes"
|
||||
|
||||
// Node represents a node view.
|
||||
type Node struct {
|
||||
ResourceViewer
|
||||
|
|
@ -65,7 +63,9 @@ func (n *Node) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
details.SetTextColor(n.App().Styles.FgColor())
|
||||
details.SetText(colorizeYAML(n.App().Styles.Views().Yaml, raw))
|
||||
details.ScrollToBeginning()
|
||||
n.App().inject(details)
|
||||
if err := n.App().inject(details); err != nil {
|
||||
n.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ func (n *Namespace) bindKeys(aa ui.KeyActions) {
|
|||
|
||||
func (n *Namespace) switchNs(app *App, _, res, sel string) {
|
||||
n.useNamespace(sel)
|
||||
app.gotoResource("po")
|
||||
if err := app.gotoResource("po"); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Namespace) useNsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
podTitle = "Pods"
|
||||
shellCheck = "command -v bash >/dev/null && exec bash || exec sh"
|
||||
)
|
||||
const shellCheck = "command -v bash >/dev/null && exec bash || exec sh"
|
||||
|
||||
// Pod represents a pod viewer.
|
||||
type Pod struct {
|
||||
|
|
@ -59,7 +56,9 @@ func (p *Pod) showContainers(app *App, ns, gvr, path string) {
|
|||
log.Debug().Msgf("SHOW CONTAINERS %q -- %q -- %q", gvr, ns, path)
|
||||
co := NewContainer(client.GVR("containers"))
|
||||
co.SetContextFn(p.podContext)
|
||||
app.inject(co)
|
||||
if err := app.inject(co); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pod) podContext(ctx context.Context) context.Context {
|
||||
|
|
@ -124,7 +123,9 @@ func (p *Pod) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
||||
p.shellIn(sel, t)
|
||||
})
|
||||
p.App().inject(picker)
|
||||
if err := p.App().inject(picker); err != nil {
|
||||
p.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,23 +10,16 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
policyTitle = "Policy"
|
||||
group = "Group"
|
||||
user = "User"
|
||||
sa = "ServiceAccount"
|
||||
allVerbs = "*"
|
||||
group = "Group"
|
||||
user = "User"
|
||||
sa = "ServiceAccount"
|
||||
allVerbs = "*"
|
||||
)
|
||||
|
||||
type (
|
||||
namespacedRole struct {
|
||||
ns, role string
|
||||
}
|
||||
|
||||
// Policy presents a RBAC policy viewer.
|
||||
Policy struct {
|
||||
ResourceViewer
|
||||
}
|
||||
)
|
||||
// Policy presents a RBAC policy viewer.
|
||||
type Policy struct {
|
||||
ResourceViewer
|
||||
}
|
||||
|
||||
// NewPolicy returns a new viewer.
|
||||
func NewPolicy(gvr client.GVR) *Policy {
|
||||
|
|
@ -44,27 +37,9 @@ func (p *Policy) Name() string {
|
|||
return "policy"
|
||||
}
|
||||
|
||||
// func (p *Policy) Start() {
|
||||
// p.Stop()
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
// p.cancel = cancel
|
||||
// go func(ctx context.Context) {
|
||||
// for {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return
|
||||
// case <-time.After(time.Duration(p.app.Config.K9s.GetRefreshRate()) * time.Second):
|
||||
// p.refresh()
|
||||
// }
|
||||
// }
|
||||
// }(ctx)
|
||||
// }
|
||||
|
||||
func (p *Policy) bindKeys(aa ui.KeyActions) {
|
||||
aa.Delete(ui.KeyShiftA, tcell.KeyCtrlSpace, ui.KeySpace)
|
||||
aa.Add(ui.KeyActions{
|
||||
// tcell.KeyEscape: ui.NewKeyAction("Back", p.resetCmd, false),
|
||||
// ui.KeySlash: ui.NewKeyAction("Filter", p.activateCmd, false),
|
||||
ui.KeyShiftP: ui.NewKeyAction("Sort Namespace", p.GetTable().SortColCmd(0, true), false),
|
||||
ui.KeyShiftN: ui.NewKeyAction("Sort Name", p.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftO: ui.NewKeyAction("Sort Group", p.GetTable().SortColCmd(2, true), false),
|
||||
|
|
@ -72,237 +47,6 @@ func (p *Policy) bindKeys(aa ui.KeyActions) {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *Policy) getTitle() string {
|
||||
// BOZO!!
|
||||
// return fmt.Sprintf(rbacTitleFmt, policyTitle, p.subjectKind+":"+p.subjectName, p.GetRowCount())
|
||||
return ""
|
||||
}
|
||||
|
||||
// func (p *Policy) refresh() {
|
||||
// log.Debug().Msgf(">>>>>>>>>>>>>>> Refreshing Policies")
|
||||
// // BOZO!!
|
||||
// defer func(t time.Time) {
|
||||
// log.Debug().Msgf("Policy Refresh elapsed %v", time.Since(t))
|
||||
// }(time.Now())
|
||||
|
||||
// data, err := p.reconcile()
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msgf("Refresh for %s:%s", p.subjectKind, p.subjectName)
|
||||
// p.app.Flash().Err(err)
|
||||
// }
|
||||
// p.app.QueueUpdateDraw(func() {
|
||||
// p.Update(data)
|
||||
// })
|
||||
// }
|
||||
|
||||
// func (p *Policy) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
// if !p.GetTable().SearchBuff().Empty() {
|
||||
// p.GetTable().SearchBuff().Reset()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// return p.backCmd(evt)
|
||||
// }
|
||||
|
||||
// func (p *Policy) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
// if p.cancel != nil {
|
||||
// p.cancel()
|
||||
// }
|
||||
|
||||
// if p.SearchBuff().IsActive() {
|
||||
// p.SearchBuff().Reset()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// return p.app.PrevCmd(evt)
|
||||
// }
|
||||
|
||||
// func (p *Policy) reconcile() (render.TableData, error) {
|
||||
// // BOZO!!
|
||||
// defer func(t time.Time) {
|
||||
// log.Debug().Msgf("Policy Reconcile elapsed %v", time.Since(t))
|
||||
// }(time.Now())
|
||||
|
||||
// var table render.TableData
|
||||
|
||||
// evts, errs := p.fetchClusterRoleBindings()
|
||||
// if len(errs) > 0 {
|
||||
// for _, err := range errs {
|
||||
// log.Error().Err(err).Msg("Unable to find cluster policies")
|
||||
// }
|
||||
// return table, errs[0]
|
||||
// }
|
||||
|
||||
// nevts, errs := p.namespacedPolicies()
|
||||
// if len(errs) > 0 {
|
||||
// for _, err := range errs {
|
||||
// log.Error().Err(err).Msg("Unable to find cluster policies")
|
||||
// }
|
||||
// return table, errs[0]
|
||||
// }
|
||||
|
||||
// for _, v := range nevts {
|
||||
// evts = append(evts, v)
|
||||
// }
|
||||
|
||||
// return buildTable(p, evts), nil
|
||||
// }
|
||||
|
||||
// Protocol...
|
||||
|
||||
// func (p *Policy) Header() render.HeaderRow {
|
||||
// return render.Policy{}.Header(render.AllNamespaces)
|
||||
// }
|
||||
|
||||
// func (p *Policy) GetCache() render.RowEvents {
|
||||
// return p.cache
|
||||
// }
|
||||
|
||||
// func (p *Policy) SetCache(evts render.RowEvents) {
|
||||
// p.cache = evts
|
||||
// }
|
||||
|
||||
// func (p *Policy) fetchClusterRoleBindings() (render.Rows, []error) {
|
||||
// var errs []error
|
||||
// oo, err := p.app.factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterrolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, append(errs, err)
|
||||
// }
|
||||
|
||||
// roles := make([]string, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var crb rbacv1.ClusterRoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
||||
// if err != nil {
|
||||
// errs = append(errs, err)
|
||||
// continue
|
||||
// }
|
||||
// for _, s := range crb.Subjects {
|
||||
// if s.Kind == p.subjectKind && s.Name == p.subjectName {
|
||||
// roles = append(roles, crb.RoleRef.Name)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// rows := make(render.Rows, 0, len(oo))
|
||||
// for _, role := range roles {
|
||||
// o, err := p.app.factory.Get(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterroles", role, labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, append(errs, err)
|
||||
// }
|
||||
// var cr rbacv1.ClusterRole
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
||||
// if err != nil {
|
||||
// errs = append(errs, err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// for _, v := range p.parseRules("*", "CR:"+role, cr.Rules) {
|
||||
// rows = append(rows, v)
|
||||
// }
|
||||
// }
|
||||
|
||||
// return rows, errs
|
||||
// }
|
||||
|
||||
// func (p *Policy) fetchRoleBindings() ([]namespacedRole, error) {
|
||||
// oo, err := p.app.factory.List(render.AllNamespaces, "rbac.authorization.k8s.io/v1/rolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// rr := make([]namespacedRole, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var rb rbacv1.RoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, s := range rb.Subjects {
|
||||
// if s.Kind == p.subjectKind && s.Name == p.subjectName {
|
||||
// rr = append(rr, namespacedRole{rb.Namespace, rb.RoleRef.Name})
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return rr, nil
|
||||
// }
|
||||
|
||||
// func (p *Policy) fetchClusterRoles(errs []error, rr []namespacedRole) (render.Rows, []error) {
|
||||
// rows := make(render.Rows, 0, len(rr))
|
||||
// for _, r := range rr {
|
||||
// o, err := p.app.factory.Get(r.ns, "rbac.authorization.k8s.io/v1/clusterroles", r.role, labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, append(errs, err)
|
||||
// }
|
||||
|
||||
// var cr rbacv1.ClusterRole
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
||||
// if err != nil {
|
||||
// errs = append(errs, err)
|
||||
// continue
|
||||
// }
|
||||
// rows = append(rows, p.parseRules(r.ns, "RO:"+r.role, cr.Rules)...)
|
||||
// }
|
||||
|
||||
// return rows, errs
|
||||
// }
|
||||
|
||||
// func (p *Policy) namespacedPolicies() (render.Rows, []error) {
|
||||
// var errs []error
|
||||
// roles, err := p.fetchRoleBindings()
|
||||
// if err != nil {
|
||||
// errs = append(errs, err)
|
||||
// }
|
||||
|
||||
// return p.fetchClusterRoles(errs, roles)
|
||||
// }
|
||||
|
||||
// func (p *Policy) parseRules(ns, binding string, rules []rbacv1.PolicyRule) render.Rows {
|
||||
// m := make(render.Rows, 0, len(rules))
|
||||
// for _, r := range rules {
|
||||
// for _, grp := range r.APIGroups {
|
||||
// for _, res := range r.Resources {
|
||||
// k := res
|
||||
// if grp != "" {
|
||||
// k = res + "." + grp
|
||||
// }
|
||||
// for _, na := range r.ResourceNames {
|
||||
// n := fqn(k, na)
|
||||
// m = append(m, render.Row{
|
||||
// ID: fqn(ns, n),
|
||||
// Fields: append(policyRow(ns, n, grp, binding), asVerbs(r.Verbs)...),
|
||||
// })
|
||||
// }
|
||||
// m = append(m, render.Row{
|
||||
// ID: fqn(ns, k),
|
||||
// Fields: append(policyRow(ns, k, grp, binding), asVerbs(r.Verbs)...),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// for _, nres := range r.NonResourceURLs {
|
||||
// if nres[0] != '/' {
|
||||
// nres = "/" + nres
|
||||
// }
|
||||
// m = append(m, render.Row{
|
||||
// ID: fqn(ns, nres),
|
||||
// Fields: append(policyRow(ns, nres, "", binding), asVerbs(r.Verbs)...),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// return m
|
||||
// }
|
||||
|
||||
func policyRow(ns, res, grp, binding string) render.Fields {
|
||||
if grp != "" {
|
||||
grp = toGroup(grp)
|
||||
}
|
||||
|
||||
r := make(render.Fields, 0, len(render.Policy{}.Header(render.AllNamespaces)))
|
||||
return append(r, ns, res, grp, binding)
|
||||
}
|
||||
|
||||
func mapSubject(subject string) string {
|
||||
switch subject {
|
||||
case "g":
|
||||
|
|
@ -314,23 +58,6 @@ func mapSubject(subject string) string {
|
|||
}
|
||||
}
|
||||
|
||||
// func showSAPolicy(app *App, _, _, selection string) {
|
||||
// _, n := client.Namespaced(selection)
|
||||
// subject, err := mapFuSubject("ServiceAccount")
|
||||
// if err != nil {
|
||||
// app.Flash().Err(err)
|
||||
// return
|
||||
// }
|
||||
// app.inject(NewPolicy(app, subject, n))
|
||||
// }
|
||||
|
||||
func toGroup(g string) string {
|
||||
if g == "" {
|
||||
return "v1"
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func hasVerb(verbs []string, verb string) bool {
|
||||
if len(verbs) == 1 && verbs[0] == allVerbs {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -13,15 +13,11 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
portForwardTitle = "PortForwards"
|
||||
promptPage = "prompt"
|
||||
)
|
||||
const promptPage = "prompt"
|
||||
|
||||
// PortForward presents active portforward viewer.
|
||||
type PortForward struct {
|
||||
|
|
@ -49,37 +45,6 @@ func (p *PortForward) portForwardContext(ctx context.Context) context.Context {
|
|||
return context.WithValue(ctx, internal.KeyBenchCfg, p.App().Bench)
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// // Start runs the refresh loop.
|
||||
// func (p *PortForward) Start() {
|
||||
// path := ui.BenchConfig(p.App().Config.K9s.CurrentCluster)
|
||||
// var ctx context.Context
|
||||
// ctx, p.cancelFn = context.WithCancel(context.Background())
|
||||
// if err := watchFS(ctx, p.App(), config.K9sHome, path, p.reload); err != nil {
|
||||
// p.App().Flash().Errf("RuRoh! Unable to watch benchmarks directory %s : %s", config.K9sHome, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Name returns the component name.
|
||||
// func (p *PortForward) Name() string {
|
||||
// return portForwardTitle
|
||||
// }
|
||||
|
||||
// func (p *PortForward) reload() {
|
||||
// path := ui.BenchConfig(p.App().Config.K9s.CurrentCluster)
|
||||
// log.Debug().Msgf("Reloading Config %s", path)
|
||||
// if err := p.App().Bench.Reload(path); err != nil {
|
||||
// p.App().Flash().Err(err)
|
||||
// }
|
||||
// p.refresh()
|
||||
// }
|
||||
|
||||
// func (p *PortForward) refresh() {
|
||||
// p.Update(p.hydrate())
|
||||
// p.App().SetFocus(p)
|
||||
// p.UpdateTitle()
|
||||
// }
|
||||
|
||||
func (p *PortForward) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyEnter: ui.NewKeyAction("Benchmarks", p.showBenchCmd, true),
|
||||
|
|
@ -94,7 +59,9 @@ func (p *PortForward) bindKeys(aa ui.KeyActions) {
|
|||
}
|
||||
|
||||
func (p *PortForward) showBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
p.App().inject(NewBenchmark("benchmarks"))
|
||||
if err := p.App().inject(NewBenchmark("benchmarks")); err != nil {
|
||||
p.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -193,62 +160,9 @@ func (p *PortForward) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
// func (p *PortForward) hydrate() render.TableData {
|
||||
// var re render.Forward
|
||||
|
||||
// data := render.TableData{
|
||||
// Header: re.Header(render.AllNamespaces),
|
||||
// RowEvents: make(render.RowEvents, 0, len(p.App().forwarders)),
|
||||
// Namespace: render.AllNamespaces,
|
||||
// }
|
||||
|
||||
// containers := p.App().Bench.Benchmarks.Containers
|
||||
// for _, f := range p.App().forwarders {
|
||||
// fqn := containerID(f.Path(), f.Container())
|
||||
// cfg := benchCfg{
|
||||
// c: p.App().Bench.Benchmarks.Defaults.C,
|
||||
// n: p.App().Bench.Benchmarks.Defaults.N,
|
||||
// }
|
||||
// if config, ok := containers[fqn]; ok {
|
||||
// cfg.c, cfg.n = config.C, config.N
|
||||
// cfg.host, cfg.path = config.HTTP.Host, config.HTTP.Path
|
||||
// }
|
||||
|
||||
// var row render.Row
|
||||
// fwd := forwarder{
|
||||
// Forwarder: f,
|
||||
// BenchConfigurator: cfg,
|
||||
// }
|
||||
// if err := re.Render(fwd, render.AllNamespaces, &row); err != nil {
|
||||
// log.Error().Err(err).Msgf("PortForward render failed")
|
||||
// continue
|
||||
// }
|
||||
// data.RowEvents = append(data.RowEvents, render.RowEvent{Kind: render.EventAdd, Row: row})
|
||||
// }
|
||||
|
||||
// return data
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
// var _ render.PortForwarder = forwarder{}
|
||||
|
||||
// type forwarder struct {
|
||||
// render.Forwarder
|
||||
// render.BenchConfigurator
|
||||
// }
|
||||
|
||||
// type benchCfg struct {
|
||||
// c, n int
|
||||
// host, path string
|
||||
// }
|
||||
|
||||
// func (b benchCfg) C() int { return b.c }
|
||||
// func (b benchCfg) N() int { return b.n }
|
||||
// func (b benchCfg) Host() string { return b.host }
|
||||
// func (b benchCfg) HttpPath() string { return b.path }
|
||||
|
||||
func defaultConfig() config.BenchConfig {
|
||||
return config.BenchConfig{
|
||||
C: config.DefaultC,
|
||||
|
|
@ -279,36 +193,3 @@ func showModal(p *ui.Pages, msg string, ok func()) {
|
|||
func dismissModal(p *ui.Pages) {
|
||||
p.RemovePage(promptPage)
|
||||
}
|
||||
|
||||
func watchFS(ctx context.Context, app *App, dir, file string, cb func()) error {
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case evt := <-w.Events:
|
||||
log.Debug().Msgf("FS %s event %v", file, evt.Name)
|
||||
if file == "" || evt.Name == file {
|
||||
log.Debug().Msgf("Capturing Event %#v", evt)
|
||||
app.QueueUpdateDraw(func() {
|
||||
cb()
|
||||
})
|
||||
}
|
||||
case err := <-w.Errors:
|
||||
log.Info().Err(err).Msgf("FS %s watcher failed", dir)
|
||||
return
|
||||
case <-ctx.Done():
|
||||
log.Debug().Msgf("<<FS %s WATCHER DONE>>", dir)
|
||||
if err := w.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("Closing portforward watcher")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w.Add(dir)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,19 +9,11 @@ import (
|
|||
"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 (
|
||||
|
|
@ -62,263 +54,47 @@ func NewRbac(gvr client.GVR) ResourceViewer {
|
|||
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 {
|
||||
// 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
|
||||
// }
|
||||
// data, err := r.reconcile(r.roleName, r.roleType)
|
||||
|
||||
// var crb rbacv1.ClusterRoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msgf("Refresh for %s:%d", r.roleName, r.roleType)
|
||||
// r.app.Flash().Err(err)
|
||||
// app.Flash().Errf("Unable to retrieve clusterrolebindings for %s", path)
|
||||
// return
|
||||
// }
|
||||
// r.Update(data)
|
||||
// r.UpdateTitle()
|
||||
|
||||
// // 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 (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)
|
||||
v.SetContextFn(rbacCtxt(gvr, path))
|
||||
|
||||
if err := app.inject(v); err != nil {
|
||||
app.Flash().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func rbacCtxt(app *App, gvr, path string) ContextFunc {
|
||||
func rbacCtxt(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)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -16,16 +15,6 @@ func ToResource(o *unstructured.Unstructured, obj interface{}) error {
|
|||
return runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &obj)
|
||||
}
|
||||
|
||||
func showCRD(app *App, ns, resource, selection string) {
|
||||
log.Debug().Msgf("Launching CRD %q -- %q -- %q", ns, resource, selection)
|
||||
tokens := strings.Split(selection, ".")
|
||||
_ = tokens
|
||||
panic("NYI")
|
||||
// if !app.gotoResource(tokens[0]) {
|
||||
// app.Flash().Errf("Goto %s failed", tokens[0])
|
||||
// }
|
||||
}
|
||||
|
||||
func loadAliases() error {
|
||||
if err := aliases.Load(); err != nil {
|
||||
return err
|
||||
|
|
@ -57,8 +46,6 @@ func loadCustomViewers() MetaViewers {
|
|||
miscRes(m)
|
||||
appsRes(m)
|
||||
rbacRes(m)
|
||||
extRes(m)
|
||||
netRes(m)
|
||||
batchRes(m)
|
||||
|
||||
return m
|
||||
|
|
@ -80,30 +67,6 @@ func coreRes(vv MetaViewers) {
|
|||
vv["v1/secrets"] = MetaViewer{
|
||||
viewerFn: NewSecret,
|
||||
}
|
||||
|
||||
// vv["v1/serviceaccounts"] = MetaViewer{
|
||||
// listFn: resource.NewServiceAccountList,
|
||||
// enterFn: showSAPolicy,
|
||||
// }
|
||||
// vv["v1/configmaps"] = MetaViewer{
|
||||
// listFn: resource.NewConfigMapList,
|
||||
// }
|
||||
// vv["v1/persistentvolumes"] = MetaViewer{
|
||||
// listFn: resource.NewPersistentVolumeList,
|
||||
// }
|
||||
// vv["v1/persistentvolumeclaims"] = MetaViewer{
|
||||
// listFn: resource.NewPersistentVolumeClaimList,
|
||||
// }
|
||||
// vv["v1/endpoints"] = MetaViewer{
|
||||
// listFn: resource.NewEndpointsList,
|
||||
// }
|
||||
// vv["v1/events"] = MetaViewer{
|
||||
// listFn: resource.NewEventList,
|
||||
// }
|
||||
// vv["v1/replicationcontrollers"] = MetaViewer{
|
||||
// viewFn: NewReplicationController,
|
||||
// listFn: resource.NewReplicationControllerList,
|
||||
// }
|
||||
}
|
||||
|
||||
func miscRes(vv MetaViewers) {
|
||||
|
|
@ -119,16 +82,6 @@ func miscRes(vv MetaViewers) {
|
|||
vv["screendumps"] = MetaViewer{
|
||||
viewerFn: NewScreenDump,
|
||||
}
|
||||
|
||||
// vv["storage.k8s.io/v1/storageclasses"] = MetaViewer{
|
||||
// listFn: resource.NewStorageClassList,
|
||||
// }
|
||||
// vv["users"] = MetaViewer{
|
||||
// viewFn: NewSubject,
|
||||
// }
|
||||
// vv["groups"] = MetaViewer{
|
||||
// viewFn: NewSubject,
|
||||
// }
|
||||
vv["benchmarks"] = MetaViewer{
|
||||
viewerFn: NewBenchmark,
|
||||
}
|
||||
|
|
@ -173,26 +126,6 @@ func rbacRes(vv MetaViewers) {
|
|||
}
|
||||
}
|
||||
|
||||
func extRes(vv MetaViewers) {
|
||||
// vv["apiextensions.k8s.io/v1/customresourcedefinitions"] = MetaViewer{
|
||||
// listFn: resource.NewCustomResourceDefinitionList,
|
||||
// enterFn: showCRD,
|
||||
// }
|
||||
// vv["apiextensions.k8s.io/v1beta1/customresourcedefinitions"] = MetaViewer{
|
||||
// listFn: resource.NewCustomResourceDefinitionList,
|
||||
// enterFn: showCRD,
|
||||
// }
|
||||
}
|
||||
|
||||
func netRes(vv MetaViewers) {
|
||||
// vv["networking.k8s.io/v1/networkpolicies"] = MetaViewer{
|
||||
// listFn: resource.NewNetworkPolicyList,
|
||||
// }
|
||||
// vv["extensions/v1beta1/ingresses"] = MetaViewer{
|
||||
// listFn: resource.NewIngressList,
|
||||
// }
|
||||
}
|
||||
|
||||
func batchRes(vv MetaViewers) {
|
||||
vv["batch/v1beta1/cronjobs"] = MetaViewer{
|
||||
viewerFn: NewCronJob,
|
||||
|
|
@ -201,16 +134,3 @@ func batchRes(vv MetaViewers) {
|
|||
viewerFn: NewJob,
|
||||
}
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func policyRes(vv MetaViewers) {
|
||||
// vv["policy/v1beta1/poddisruptionbudgets"] = MetaViewer{
|
||||
// listFn: resource.NewPDBList,
|
||||
// }
|
||||
// }
|
||||
|
||||
// func autoscalingRes(vv MetaViewers) {
|
||||
// vv["autoscaling/v1/horizontalpodautoscalers"] = MetaViewer{
|
||||
// listFn: resource.NewHorizontalPodAutoscalerV1List,
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -9,13 +9,10 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const dumpTitle = "Screen Dumps"
|
||||
|
||||
// ScreenDump presents a directory listing viewer.
|
||||
type ScreenDump struct {
|
||||
ResourceViewer
|
||||
|
|
@ -26,7 +23,6 @@ func NewScreenDump(gvr client.GVR) ResourceViewer {
|
|||
s := ScreenDump{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
// BOZO!! Rename Table
|
||||
s.GetTable().SetBorderFocusColor(tcell.ColorSteelBlue)
|
||||
s.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorRoyalBlue, tcell.AttrNone)
|
||||
s.GetTable().SetColorerFn(render.ScreenDump{}.ColorerFunc())
|
||||
|
|
@ -38,22 +34,6 @@ func NewScreenDump(gvr client.GVR) ResourceViewer {
|
|||
return &s
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// BOZO !! Need model watcher!
|
||||
// // Start starts the directory watcher.
|
||||
// func (s *ScreenDump) Start() {
|
||||
// s.Stop()
|
||||
|
||||
// s.GetTable().Actions().Delete(tcell.KeyCtrlS)
|
||||
|
||||
// s.GetTable().Start()
|
||||
// var ctx context.Context
|
||||
// ctx, s.GetTable().cancelFn = context.WithCancel(context.Background())
|
||||
// if err := s.watchDumpDir(ctx); err != nil {
|
||||
// s.App().Flash().Errf("Unable to watch screen dumps directory %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
func (s *ScreenDump) dirContext(ctx context.Context) context.Context {
|
||||
dir := filepath.Join(config.K9sDumpDir, s.App().Config.K9s.CurrentCluster)
|
||||
return context.WithValue(ctx, internal.KeyDir, dir)
|
||||
|
|
@ -68,31 +48,3 @@ func (s *ScreenDump) edit(app *App, ns, resource, path string) {
|
|||
app.Flash().Err(errors.New("Failed to launch editor"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ScreenDump) watchDumpDir(ctx context.Context) error {
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case evt := <-w.Events:
|
||||
log.Debug().Msgf("ScreenDump event detected %#v", evt)
|
||||
s.Refresh()
|
||||
case err := <-w.Errors:
|
||||
log.Error().Err(err).Msg("Dir Watcher failed")
|
||||
return
|
||||
case <-ctx.Done():
|
||||
log.Debug().Msg("!!!! ScreenDump WATCHER DONE!!")
|
||||
if err := w.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("Closing dump watcher")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w.Add(filepath.Join(config.K9sDumpDir, s.App().Config.K9s.CurrentCluster))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
details.SetTextColor(s.App().Styles.FgColor())
|
||||
details.SetText(colorizeYAML(s.App().Styles.Views().Yaml, string(raw)))
|
||||
details.ScrollToBeginning()
|
||||
s.App().inject(details)
|
||||
if err := s.App().inject(details); err != nil {
|
||||
s.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ type (
|
|||
ResourceViewer
|
||||
|
||||
subjectKind string
|
||||
cache render.RowEvents
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -27,32 +26,13 @@ type (
|
|||
func NewSubject(gvr client.GVR) ResourceViewer {
|
||||
s := Subject{ResourceViewer: NewBrowser(gvr)}
|
||||
s.GetTable().SetColorerFn(render.Subject{}.ColorerFunc())
|
||||
// BOZO!!
|
||||
// s.GetTable().SetSortCol(1, len(s.Header()), true)
|
||||
s.SetBindKeysFn(s.bindKeys)
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// // Start runs the refresh loop.
|
||||
// func (s *Subject) Start() {
|
||||
// s.Stop()
|
||||
|
||||
// var ctx context.Context
|
||||
// ctx, s.cancelFn = context.WithCancel(context.Background())
|
||||
// go func(ctx context.Context) {
|
||||
// for {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// log.Debug().Msgf("Subject:%s Watch bailing out!", s.subjectKind)
|
||||
// return
|
||||
// case <-time.After(time.Duration(s.App().Config.K9s.GetRefreshRate()) * time.Second):
|
||||
// s.refresh()
|
||||
// }
|
||||
// }
|
||||
// }(ctx)
|
||||
// }
|
||||
|
||||
// Name returns the component name
|
||||
func (s *Subject) Name() string {
|
||||
return "subjects"
|
||||
|
|
@ -62,10 +42,7 @@ func (s *Subject) bindKeys(aa ui.KeyActions) {
|
|||
aa.Delete(ui.KeyShiftA, ui.KeyShiftP, tcell.KeyCtrlSpace, ui.KeySpace)
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyEnter: ui.NewKeyAction("Policies", s.policyCmd, true),
|
||||
// BOZO!!
|
||||
// tcell.KeyEscape: ui.NewKeyAction("Back", s.resetCmd, false),
|
||||
// ui.KeySlash: ui.NewKeyAction("Filter", s.activateCmd, false),
|
||||
ui.KeyShiftK: ui.NewKeyAction("Sort Kind", s.GetTable().SortColCmd(1, true), false),
|
||||
ui.KeyShiftK: ui.NewKeyAction("Sort Kind", s.GetTable().SortColCmd(1, true), false),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -74,24 +51,12 @@ func (s *Subject) SetSubject(n string) {
|
|||
s.subjectKind = mapSubject(n)
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func (s *Subject) refresh() {
|
||||
// log.Debug().Msgf("Refreshing Subject...")
|
||||
// data, err := s.reconcile()
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msgf("Refresh for %s", s.subjectKind)
|
||||
// s.App().Flash().Err(err)
|
||||
// }
|
||||
// s.App().QueueUpdateDraw(func() {
|
||||
// s.GetTable().Update(data)
|
||||
// })
|
||||
// }
|
||||
|
||||
func (s *Subject) policyCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !s.GetTable().RowSelected() {
|
||||
return evt
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// _, n := client.Namespaced(s.GetSelectedItem())
|
||||
// subject, err := mapFuSubject(s.subjectKind)
|
||||
// if err != nil {
|
||||
|
|
@ -103,184 +68,3 @@ func (s *Subject) policyCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (s *Subject) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
// if !s.SearchBuff().Empty() {
|
||||
// s.SearchBuff().Reset()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// return s.backCmd(evt)
|
||||
// }
|
||||
|
||||
// func (s *Subject) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
// if s.SearchBuff().IsActive() {
|
||||
// s.SearchBuff().Reset()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// return s.App().PrevCmd(evt)
|
||||
// }
|
||||
|
||||
// func (s *Subject) reconcile() (render.TableData, error) {
|
||||
// var table render.TableData
|
||||
// if s.App().Conn() == nil {
|
||||
// return table, nil
|
||||
// }
|
||||
|
||||
// rows, err := s.fetchClusterRoleBindings()
|
||||
// if err != nil {
|
||||
// return table, err
|
||||
// }
|
||||
|
||||
// nrows, err := s.fetchRoleBindings()
|
||||
// if err != nil {
|
||||
// return table, err
|
||||
// }
|
||||
// for k, v := range nrows {
|
||||
// rows[k] = v
|
||||
// }
|
||||
|
||||
// return buildTable(s, rows), nil
|
||||
// }
|
||||
|
||||
// func (s *Subject) Header() render.HeaderRow {
|
||||
// return render.Subject{}.Header(render.AllNamespaces)
|
||||
// }
|
||||
|
||||
// func (s *Subject) GetCache() render.RowEvents {
|
||||
// return s.cache
|
||||
// }
|
||||
|
||||
// func (s *Subject) SetCache(rows render.RowEvents) {
|
||||
// s.cache = rows
|
||||
// }
|
||||
|
||||
// func buildTable(c TableInfo, rows render.Rows) render.TableData {
|
||||
// table := render.TableData{
|
||||
// Header: c.Header(),
|
||||
// Namespace: "*",
|
||||
// }
|
||||
|
||||
// cache := c.GetCache()
|
||||
// if len(cache) == 0 {
|
||||
// cache := make(render.RowEvents, 0, len(rows))
|
||||
// for _, row := range rows {
|
||||
// cache = append(cache, render.RowEvent{Kind: render.EventAdd, Row: row})
|
||||
// }
|
||||
// table.RowEvents = cache
|
||||
// return table
|
||||
// }
|
||||
|
||||
// for _, row := range rows {
|
||||
// idx, ok := cache.FindIndex(row.ID)
|
||||
// if !ok {
|
||||
// cache = append(cache, render.RowEvent{Kind: render.EventAdd, Row: row})
|
||||
// continue
|
||||
// }
|
||||
|
||||
// old := cache[idx].Row
|
||||
// deltas := make(render.DeltaRow, len(row.Fields))
|
||||
// if reflect.DeepEqual(old, row) {
|
||||
// cache[idx].Kind = render.EventUnchanged
|
||||
// cache[idx].Deltas = deltas
|
||||
// continue
|
||||
// }
|
||||
|
||||
// cache[idx].Kind = render.EventUpdate
|
||||
// for i, field := range old.Fields {
|
||||
// if field != row.Fields[i] {
|
||||
// deltas[i] = field
|
||||
// }
|
||||
// }
|
||||
// cache[idx].Deltas = deltas
|
||||
// }
|
||||
|
||||
// for _, row := range rows {
|
||||
// if _, ok := cache.FindIndex(row.ID); !ok {
|
||||
// cache.Delete(row.ID)
|
||||
// }
|
||||
// }
|
||||
// table.RowEvents = cache
|
||||
|
||||
// return table
|
||||
// }
|
||||
|
||||
// func (s *Subject) fetchClusterRoleBindings() (render.Rows, error) {
|
||||
// s.App().factory.Preload(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterroles")
|
||||
// oo, err := s.App().factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterrolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// rows := make(render.Rows, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var crb rbacv1.ClusterRoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, subject := range crb.Subjects {
|
||||
// if subject.Kind != s.subjectKind {
|
||||
// continue
|
||||
// }
|
||||
// rows = append(rows, render.Row{
|
||||
// ID: subject.Name,
|
||||
// Fields: render.Fields{subject.Name, "ClusterRoleBinding", crb.Name},
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// return rows, nil
|
||||
// }
|
||||
|
||||
// func (s *Subject) fetchRoleBindings() (render.Rows, error) {
|
||||
// s.App().factory.Preload(render.ClusterScope, "rbac.authorization.k8s.io/v1/clusterroles")
|
||||
// oo, err := s.App().factory.List(render.ClusterScope, "rbac.authorization.k8s.io/v1/rolebindings", labels.Everything())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// rows := make(render.Rows, 0, len(oo))
|
||||
// for _, o := range oo {
|
||||
// var rb rbacv1.RoleBinding
|
||||
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, subject := range rb.Subjects {
|
||||
// if subject.Kind == s.subjectKind {
|
||||
// rows = append(rows, render.Row{
|
||||
// ID: subject.Name,
|
||||
// Fields: render.Fields{subject.Name, "RoleBinding", rb.Name},
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return rows, nil
|
||||
// }
|
||||
|
||||
// func mapCmdSubject(subject string) string {
|
||||
// switch subject {
|
||||
// case "groups":
|
||||
// return group
|
||||
// case "sas":
|
||||
// return sa
|
||||
// default:
|
||||
// return user
|
||||
// }
|
||||
// }
|
||||
|
||||
// func mapFuSubject(subject string) (string, error) {
|
||||
// switch subject {
|
||||
// case group:
|
||||
// return "g", nil
|
||||
// case sa:
|
||||
// return "s", nil
|
||||
// case user:
|
||||
// return "u", nil
|
||||
// default:
|
||||
// return "", fmt.Errorf("Unknown subject %q should be one of user, group, serviceaccount", subject)
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -93,15 +93,6 @@ func (t *Table) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) setFilterFn(fn func(string)) {
|
||||
t.filterFn = fn
|
||||
|
||||
cmd := t.SearchBuff().String()
|
||||
if ui.IsLabelSelector(cmd) && t.filterFn != nil {
|
||||
t.filterFn(ui.TrimLabelSelector(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) bindKeys() {
|
||||
t.Actions().Add(ui.KeyActions{
|
||||
ui.KeySpace: ui.NewKeyAction("Mark", t.markCmd, true),
|
||||
|
|
|
|||
|
|
@ -65,12 +65,7 @@ func saveTable(cluster, title, path string, data render.TableData) (string, erro
|
|||
}()
|
||||
|
||||
w := csv.NewWriter(out)
|
||||
// BOZO!! Method on header
|
||||
headers := make([]string, len(data.Header))
|
||||
for i, h := range data.Header {
|
||||
headers[i] = h.Name
|
||||
}
|
||||
if err := w.Write([]string(headers)); err != nil {
|
||||
if err := w.Write(data.Header.Columns()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
di "k8s.io/client-go/dynamic/dynamicinformer"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/informers/internalinterfaces"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -22,46 +21,13 @@ const (
|
|||
clusterScope = "-"
|
||||
)
|
||||
|
||||
// BOZO!!
|
||||
// // Authorizer checks what a user can or cannot do to a resource.
|
||||
// type Authorizer interface {
|
||||
// // CanI returns true if the user can use these actions for a given resource.
|
||||
// CanI(ns, gvr string, verbs []string) (bool, error)
|
||||
// }
|
||||
|
||||
// type Connection interface {
|
||||
// Authorizer
|
||||
|
||||
// // DialOrDie dials client api.
|
||||
// DialOrDie() kubernetes.Interface
|
||||
|
||||
// // MXDial dials metrics api.
|
||||
// MXDial() (*versioned.Clientset, error)
|
||||
|
||||
// // DynDialOrDie dials dynamic client api.
|
||||
// DynDialOrDie() dynamic.Interface
|
||||
|
||||
// // RestConfigOrDie return a client configuration.
|
||||
// RestConfigOrDie() *restclient.Config
|
||||
|
||||
// // Config returns the current kubeconfig.
|
||||
// Config() *k8s.Config
|
||||
|
||||
// // CachedDiscovery returns a cached client.
|
||||
// CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||
|
||||
// // SwithContextOrDie switch to a new kube context.
|
||||
// SwitchContextOrDie(ctx string)
|
||||
// }
|
||||
|
||||
// Factory tracks various resource informers.
|
||||
type Factory struct {
|
||||
factories map[string]di.DynamicSharedInformerFactory
|
||||
client client.Connection
|
||||
stopChan chan struct{}
|
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||
activeNS string
|
||||
forwarders Forwarders
|
||||
factories map[string]di.DynamicSharedInformerFactory
|
||||
client client.Connection
|
||||
stopChan chan struct{}
|
||||
activeNS string
|
||||
forwarders Forwarders
|
||||
}
|
||||
|
||||
// NewFactory returns a new informers factory.
|
||||
|
|
@ -259,14 +225,6 @@ func (f *Factory) ensureFactory(ns string) di.DynamicSharedInformerFactory {
|
|||
return f.factories[ns]
|
||||
}
|
||||
|
||||
func (f *Factory) register(gvr, ns string, stopChan <-chan struct{}) error {
|
||||
log.Debug().Msgf("Registering GVR %q - %s", ns, gvr)
|
||||
f.factories[ns].ForResource(toGVR(gvr))
|
||||
f.factories[ns].Start(stopChan)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toGVR(gvr string) schema.GroupVersionResource {
|
||||
log.Debug().Msgf("GVR -- %q", gvr)
|
||||
tokens := strings.Split(gvr, "/")
|
||||
|
|
|
|||
Loading…
Reference in New Issue