perf + cleanup
parent
ce9e5ee5cb
commit
8af75df9f8
|
|
@ -77,45 +77,42 @@ func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
|
|||
}
|
||||
}
|
||||
|
||||
func makeKey(ns, gvr string, vv []string) string {
|
||||
func makeCacheKey(ns, gvr string, vv []string) string {
|
||||
return ns + ":" + gvr + "::" + strings.Join(vv, ",")
|
||||
}
|
||||
|
||||
// CanI checks if user has access to a certain resource.
|
||||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (bool, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("AUTH elapsed %v", time.Since(t))
|
||||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
|
||||
key := makeCacheKey(ns, gvr, verbs)
|
||||
defer func(t time.Time) string {
|
||||
log.Debug().Msgf("AUTH elapsed %t--%q %v", auth, key, time.Since(t))
|
||||
return "s"
|
||||
}(time.Now())
|
||||
|
||||
log.Debug().Msgf("AUTH %q:%q -- %v", ns, gvr, verbs)
|
||||
sar := makeSAR(ns, gvr)
|
||||
|
||||
key := makeKey(ns, gvr, verbs)
|
||||
if v, ok := a.cache.Get(key); ok {
|
||||
if auth, ok := v.(bool); ok {
|
||||
if auth, ok = v.(bool); ok {
|
||||
return auth, nil
|
||||
}
|
||||
}
|
||||
|
||||
dial := a.DialOrDie().AuthorizationV1().SelfSubjectAccessReviews()
|
||||
dial, sar := a.DialOrDie().AuthorizationV1().SelfSubjectAccessReviews(), makeSAR(ns, gvr)
|
||||
for _, v := range verbs {
|
||||
sar.Spec.ResourceAttributes.Verb = v
|
||||
resp, err := dial.Create(sar)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf(" Dial Failed!")
|
||||
a.cache.Add(key, false, cacheExpiry)
|
||||
return false, err
|
||||
return auth, err
|
||||
}
|
||||
if !resp.Status.Allowed {
|
||||
log.Debug().Msgf(" NO %q ;(", v)
|
||||
a.cache.Add(key, false, cacheExpiry)
|
||||
return false, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr)
|
||||
return auth, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug().Msgf(" YES!")
|
||||
auth = true
|
||||
a.cache.Add(key, true, cacheExpiry)
|
||||
return true, nil
|
||||
return
|
||||
}
|
||||
|
||||
// CurrentNamespaceName return namespace name set via either cli arg or cluster config.
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func (m *MetricsServer) FetchNodesMetrics() (*mv1beta1.NodeMetricsList, error) {
|
|||
return &mx, fmt.Errorf("No metrics-server detected on cluster")
|
||||
}
|
||||
|
||||
auth, err := m.CanI("", "metrics.k8s.io/v1beta1/nodes", []string{"list"})
|
||||
auth, err := m.CanI("", "metrics.k8s.io/v1beta1/nodes", ListAccess)
|
||||
if !auth || err != nil {
|
||||
return &mx, err
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ func (m *MetricsServer) FetchPodsMetrics(ns string) (*mv1beta1.PodMetricsList, e
|
|||
ns = AllNamespaces
|
||||
}
|
||||
|
||||
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", []string{"list"})
|
||||
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", ListAccess)
|
||||
if !auth || err != nil {
|
||||
return &mx, err
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ func (m *MetricsServer) FetchPodMetrics(ns, sel string) (*mv1beta1.PodMetrics, e
|
|||
if ns == NamespaceAll {
|
||||
ns = AllNamespaces
|
||||
}
|
||||
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", []string{"get"})
|
||||
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", GetAccess)
|
||||
if !auth || err != nil {
|
||||
return &mx, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,34 @@ const (
|
|||
ClusterScope = "-"
|
||||
)
|
||||
|
||||
const (
|
||||
// CreateVerb represents create access on a resource.
|
||||
CreateVerb = "create"
|
||||
// UpdateVerb represents an update access on a resource.
|
||||
UpdateVerb = "update"
|
||||
// UpdateVerb represents a patch access on a resource.
|
||||
PatchVerb = "patch"
|
||||
// DeleteVerb represents a delete access on a resource.
|
||||
DeleteVerb = "delete"
|
||||
// GetVerb represents a get access on a resource.
|
||||
GetVerb = "get"
|
||||
// ListVerb represents a list access on a resource.
|
||||
ListVerb = "list"
|
||||
// WatchVerb represents a watch access on a resource.
|
||||
WatchVerb = "watch"
|
||||
)
|
||||
|
||||
var (
|
||||
// GetAccess reads a resource.
|
||||
GetAccess = []string{GetVerb}
|
||||
// ListAccess list resources.
|
||||
ListAccess = []string{ListVerb}
|
||||
// MonitorAcces monitors a collection of resources.
|
||||
MonitorAccess = []string{ListVerb, WatchVerb}
|
||||
// ReadAllAccess represents an all read access to a resource.
|
||||
ReadAllAccess = []string{GetVerb, ListVerb, WatchVerb}
|
||||
)
|
||||
|
||||
// Connection represents a Kubenetes apiserver connection.
|
||||
// BOZO!! Refactor!
|
||||
type Connection interface {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
|
|
@ -29,32 +30,29 @@ type Container struct {
|
|||
|
||||
// List returns a collection of containers.
|
||||
func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
||||
path, ok := ctx.Value(internal.KeyPath).(string)
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no context path for %q", c.gvr)
|
||||
}
|
||||
ns, _ := render.Namespaced(path)
|
||||
o, err := c.Factory.Get("v1/pods", path, true, labels.Everything())
|
||||
po, err := c.fetchPod(fqn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var po v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
|
||||
mx := client.NewMetricsServer(c.Client())
|
||||
ns, _ := render.Namespaced(fqn)
|
||||
var pmx *mv1beta1.PodMetrics
|
||||
if c.Client() != nil {
|
||||
var err error
|
||||
pmx, err = mx.FetchPodMetrics(ns, po.Name)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", ns, po.Name)
|
||||
if c.Client().HasMetrics() {
|
||||
mx := client.NewMetricsServer(c.Client())
|
||||
if c.Client() != nil {
|
||||
var err error
|
||||
pmx, err = mx.FetchPodMetrics(ns, po.Name)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", ns, po.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
|
||||
for _, co := range po.Spec.InitContainers {
|
||||
res = append(res, makeContainerRes(co, po, pmx, true))
|
||||
}
|
||||
|
|
@ -87,7 +85,7 @@ func (c *Container) TailLogs(ctx context.Context, logChan chan<- string, opts Lo
|
|||
// Logs fetch container logs for a given pod and container.
|
||||
func (c *Container) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, "v1/pods:log", []string{"get"})
|
||||
auth, err := c.Client().CanI(ns, "v1/pods:log", client.GetAccess)
|
||||
if !auth || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -99,14 +97,18 @@ func (c *Container) Logs(path string, opts *v1.PodLogOptions) (*restclient.Reque
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func makeContainerRes(co v1.Container, po v1.Pod, pmx *mv1beta1.PodMetrics, isInit bool) render.ContainerRes {
|
||||
func makeContainerRes(co v1.Container, po *v1.Pod, pmx *mv1beta1.PodMetrics, isInit bool) render.ContainerRes {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("MAKE-CO %s -- %v", co.Name, time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
cmx, err := containerMetrics(co.Name, pmx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("Container metrics for %s", co.Name)
|
||||
log.Warn().Err(err).Msgf("Fail container metrics for %s", co.Name)
|
||||
}
|
||||
|
||||
return render.ContainerRes{
|
||||
Container: co,
|
||||
Container: &co,
|
||||
Status: getContainerStatus(co.Name, po.Status),
|
||||
Metrics: cmx,
|
||||
IsInit: isInit,
|
||||
|
|
@ -114,11 +116,7 @@ func makeContainerRes(co v1.Container, po v1.Pod, pmx *mv1beta1.PodMetrics, isIn
|
|||
}
|
||||
}
|
||||
|
||||
func containerMetrics(n string, mx runtime.Object) (*mv1beta1.ContainerMetrics, error) {
|
||||
pmx, ok := mx.(*mv1beta1.PodMetrics)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expecting podmetrics but got `%T", mx)
|
||||
}
|
||||
func containerMetrics(n string, pmx *mv1beta1.PodMetrics) (*mv1beta1.ContainerMetrics, error) {
|
||||
if pmx == nil {
|
||||
return nil, fmt.Errorf("no metrics for container %s", n)
|
||||
}
|
||||
|
|
@ -136,7 +134,6 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
|||
return &c
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range status.InitContainerStatuses {
|
||||
if c.Name == co {
|
||||
return &c
|
||||
|
|
@ -145,3 +142,17 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
|
||||
o, err := c.Factory.Get("v1/pods", fqn, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var po v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &po, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,17 @@ import (
|
|||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/discovery/cached/disk"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
func TestContainerList(t *testing.T) {
|
||||
|
|
@ -29,12 +36,36 @@ func TestContainerList(t *testing.T) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func makeConn() *conn {
|
||||
return &conn{}
|
||||
}
|
||||
|
||||
func (c *conn) Config() *client.Config { return nil }
|
||||
func (c *conn) DialOrDie() kubernetes.Interface { return nil }
|
||||
func (c *conn) SwitchContextOrDie(ctx string) {}
|
||||
func (c *conn) CachedDiscoveryOrDie() *disk.CachedDiscoveryClient { return nil }
|
||||
func (c *conn) RestConfigOrDie() *restclient.Config { return nil }
|
||||
func (c *conn) MXDial() (*versioned.Clientset, error) { return nil, nil }
|
||||
func (c *conn) DynDialOrDie() dynamic.Interface { return nil }
|
||||
func (c *conn) HasMetrics() bool { return false }
|
||||
func (c *conn) IsNamespaced(n string) bool { return false }
|
||||
func (c *conn) SupportsResource(group string) bool { return false }
|
||||
func (c *conn) ValidNamespaces() ([]v1.Namespace, error) { return nil, nil }
|
||||
func (c *conn) SupportsRes(grp string, versions []string) (string, bool, error) {
|
||||
return "", false, nil
|
||||
}
|
||||
func (c *conn) ServerVersion() (*version.Info, error) { return nil, nil }
|
||||
func (c *conn) CurrentNamespaceName() (string, error) { return "", nil }
|
||||
func (c *conn) CanI(ns, gvr string, verbs []string) (bool, error) { return true, nil }
|
||||
|
||||
type podFactory struct{}
|
||||
|
||||
var _ dao.Factory = testFactory{}
|
||||
|
||||
func (f podFactory) Client() client.Connection {
|
||||
return nil
|
||||
return makeConn()
|
||||
}
|
||||
func (f podFactory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
|
||||
var m map[string]interface{}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type CronJob struct {
|
|||
// Run a CronJob.
|
||||
func (c *CronJob) Run(path string) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, "batch/v1beta1/cronjobs", []string{"get", "create"})
|
||||
auth, err := c.Client().CanI(ns, "batch/v1beta1/cronjobs", []string{client.GetVerb, client.CreateVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type Deployment struct {
|
|||
// Scale a Deployment.
|
||||
func (d *Deployment) Scale(path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", []string{"get", "update"})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", []string{client.GetVerb, client.UpdateVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func (d *Deployment) Restart(path string) error {
|
|||
}
|
||||
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", []string{"patch"})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", []string{client.PatchVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func (d *DaemonSet) Restart(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", []string{"patch"})
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", []string{client.PatchVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
|
|||
if !ok {
|
||||
log.Warn().Msgf("No label selector found in context. Listing all resources")
|
||||
}
|
||||
|
||||
if client.IsAllNamespace(ns) {
|
||||
ns = client.AllNamespaces
|
||||
}
|
||||
|
|
@ -90,7 +89,7 @@ func (g *Generic) ToYAML(path string) (string, error) {
|
|||
// Delete deletes a resource.
|
||||
func (g *Generic) Delete(path string, cascade, force bool) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := g.Client().CanI(ns, g.gvr.String(), []string{"delete"})
|
||||
auth, err := g.Client().CanI(ns, g.gvr.String(), []string{client.DeleteVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -59,7 +60,7 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|||
|
||||
// FetchNodes retrieves all nodes.
|
||||
func FetchNodes(f Factory) (*v1.NodeList, error) {
|
||||
auth, err := f.Client().CanI("", "v1/nodes", []string{"list"})
|
||||
auth, err := f.Client().CanI("", "v1/nodes", []string{client.ListVerb})
|
||||
if !auth || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", []string{"get"})
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", []string{client.GetVerb})
|
||||
if !auth || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type PortForward struct {
|
|||
// Delete a portforward.
|
||||
func (p *PortForward) Delete(path string, cascade, force bool) error {
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:portforward", []string{"delete"})
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:portforward", []string{client.DeleteVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func (p *PortForwarder) Start(path, co, address string, ports []string) (*portfo
|
|||
p.path, p.container, p.ports, p.age = path, co, ports, time.Now()
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.CanI(ns, "v1/pods", []string{"get"})
|
||||
auth, err := p.CanI(ns, "v1/pods", []string{client.GetVerb})
|
||||
if !auth || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ func (p *PortForwarder) Start(path, co, address string, ports []string) (*portfo
|
|||
return nil, fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
|
||||
}
|
||||
|
||||
auth, err = p.CanI(ns, "v1/pods:portforward", []string{"update"})
|
||||
auth, err = p.CanI(ns, "v1/pods:portforward", []string{client.UpdateVerb})
|
||||
if !auth || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type StatefulSet struct {
|
|||
// Scale a StatefulSet.
|
||||
func (s *StatefulSet) Scale(path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", []string{"get", "update"})
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", []string{client.GetVerb, client.UpdateVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ func (s *StatefulSet) Restart(path string) error {
|
|||
}
|
||||
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets", []string{"patch"})
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets", []string{client.PatchVerb})
|
||||
if !auth || err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o, err := c.Get().
|
||||
SetHeader("Accept", a).
|
||||
Namespace(ns).
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const iniRefreshRate = 300 * time.Millisecond
|
||||
|
||||
// TableListener represents a table model listener.
|
||||
type TableListener interface {
|
||||
// TableDataChanged notifies the model data changed.
|
||||
|
|
@ -167,11 +169,14 @@ func (t *Table) Peek() render.TableData {
|
|||
|
||||
func (t *Table) updater(ctx context.Context) {
|
||||
defer log.Debug().Msgf("Model canceled -- %q", t.gvr)
|
||||
|
||||
rate := iniRefreshRate
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(t.refreshRate):
|
||||
case <-time.After(rate):
|
||||
rate = t.refreshRate
|
||||
t.refresh(ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ func gatherMetrics(co ContainerRes) (c, p metric) {
|
|||
mem: ToMi(mem),
|
||||
}
|
||||
|
||||
rcpu, rmem := containerResources(co.Container)
|
||||
rcpu, rmem := containerResources(*co.Container)
|
||||
if rcpu != nil {
|
||||
p.cpu = AsPerc(toPerc(float64(cpu), float64(rcpu.MilliValue())))
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ func probe(p *v1.Probe) string {
|
|||
|
||||
// ContainerRes represents a container and its metrics.
|
||||
type ContainerRes struct {
|
||||
Container v1.Container
|
||||
Container *v1.Container
|
||||
Status *v1.ContainerStatus
|
||||
Metrics *mv1beta1.ContainerMetrics
|
||||
IsInit bool
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ func makeAge() metav1.Time {
|
|||
return metav1.Time{Time: testTime()}
|
||||
}
|
||||
|
||||
func makeContainer() v1.Container {
|
||||
return v1.Container{
|
||||
func makeContainer() *v1.Container {
|
||||
return &v1.Container{
|
||||
Name: "fred",
|
||||
Image: "img",
|
||||
Resources: v1.ResourceRequirements{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/ui/dialog"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -50,8 +49,9 @@ func (b *Browser) Init(ctx context.Context) error {
|
|||
if err = b.Table.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
ns := b.app.Config.ActiveNamespace()
|
||||
if dao.IsK8sMeta(b.meta) {
|
||||
if _, e := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR(), []string{"list", "watch"}); e != nil {
|
||||
if _, e := b.app.factory.CanForResource(ns, b.GVR(), client.MonitorAccess); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
b.setNamespace(b.App().Config.ActiveNamespace())
|
||||
b.setNamespace(ns)
|
||||
row, _ := b.GetSelection()
|
||||
if row == 0 && b.GetRowCount() > 0 {
|
||||
b.Select(1, 0)
|
||||
|
|
@ -296,7 +296,7 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
ns = client.NamespaceAll
|
||||
}
|
||||
|
||||
auth, err := b.App().factory.Client().CanI(ns, b.GVR(), watch.ReadVerbs)
|
||||
auth, err := b.App().factory.Client().CanI(ns, b.GVR(), client.MonitorAccess)
|
||||
if !auth {
|
||||
b.App().Flash().Err(err)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
|
@ -60,7 +59,7 @@ func (l *LogsExtender) showLogs(path string, prev bool) {
|
|||
log.Debug().Msgf("SHOWING LOGS path %q", path)
|
||||
// Need to load and wait for pods
|
||||
ns, _ := render.Namespaced(path)
|
||||
_, err := l.App().factory.CanForResource(ns, "v1/pods", watch.ReadVerbs)
|
||||
_, err := l.App().factory.CanForResource(ns, "v1/pods", client.MonitorAccess)
|
||||
if err != nil {
|
||||
l.App().Flash().Err(err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package watch
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
|
|
@ -18,15 +19,13 @@ const (
|
|||
clusterScope = "-"
|
||||
)
|
||||
|
||||
// ReadVerbs lists out RO verbs.
|
||||
var ReadVerbs = []string{"get", "list", "watch"}
|
||||
|
||||
// Factory tracks various resource informers.
|
||||
type Factory struct {
|
||||
factories map[string]di.DynamicSharedInformerFactory
|
||||
client client.Connection
|
||||
stopChan chan struct{}
|
||||
forwarders Forwarders
|
||||
mx sync.RWMutex
|
||||
}
|
||||
|
||||
// NewFactory returns a new informers factory.
|
||||
|
|
@ -54,6 +53,9 @@ func (f *Factory) Terminate() {
|
|||
close(f.stopChan)
|
||||
f.stopChan = nil
|
||||
}
|
||||
|
||||
f.mx.Lock()
|
||||
defer f.mx.Unlock()
|
||||
for k := range f.factories {
|
||||
delete(f.factories, k)
|
||||
}
|
||||
|
|
@ -70,7 +72,7 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
|
|||
ns = allNamespaces
|
||||
}
|
||||
log.Debug().Msgf("List %q:%q", ns, gvr)
|
||||
inf, err := f.CanForResource(ns, gvr, []string{"list", "watch"})
|
||||
inf, err := f.CanForResource(ns, gvr, client.MonitorAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -86,13 +88,18 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
|
|||
|
||||
// Get retrieves a given resource.
|
||||
func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("FACTORY-GET %q--%q elapsed %v", gvr, path, time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
ns, n := namespaced(path)
|
||||
log.Debug().Msgf("GET %q:%q::%q", ns, gvr, n)
|
||||
Debug(f, "", gvr)
|
||||
inf, err := f.CanForResource(ns, gvr, []string{"get"})
|
||||
inf, err := f.CanForResource(ns, gvr, []string{client.GetVerb})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
DumpFactory(f)
|
||||
if wait {
|
||||
f.waitForCacheSync(ns)
|
||||
}
|
||||
|
|
@ -193,6 +200,8 @@ func (f *Factory) ensureFactory(ns string) di.DynamicSharedInformerFactory {
|
|||
}
|
||||
|
||||
log.Debug().Msgf("FACTORY_NEW for ns %q", ns)
|
||||
f.mx.Lock()
|
||||
defer f.mx.Unlock()
|
||||
f.factories[ns] = di.NewFilteredDynamicSharedInformerFactory(
|
||||
f.client.DynDialOrDie(),
|
||||
defaultResync,
|
||||
|
|
@ -210,8 +219,6 @@ func (f *Factory) AddForwarder(pf Forwarder) {
|
|||
|
||||
// DeleteForwarder deletes portforward for a given container.
|
||||
func (f *Factory) DeleteForwarder(path string) {
|
||||
|
||||
f.forwarders.Dump()
|
||||
count := f.forwarders.Kill(path)
|
||||
log.Warn().Msgf("Deleted (%d) portforward for %q", count, path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func namespaced(n string) (string, string) {
|
|||
}
|
||||
|
||||
// Dump for debug.
|
||||
func Dump(f *Factory) {
|
||||
func DumpFactory(f *Factory) {
|
||||
log.Debug().Msgf("----------- FACTORIES -------------")
|
||||
for ns := range f.factories {
|
||||
log.Debug().Msgf(" Factory for NS %q", ns)
|
||||
|
|
@ -37,7 +37,7 @@ func Dump(f *Factory) {
|
|||
}
|
||||
|
||||
// Debug for debug.
|
||||
func Debug(f *Factory, ns string, gvr string) {
|
||||
func DebugFactory(f *Factory, ns string, gvr string) {
|
||||
log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
|
||||
fac, ok := f.factories[ns]
|
||||
if !ok {
|
||||
|
|
|
|||
Loading…
Reference in New Issue