fix hpas + cleanup

mine
derailed 2019-12-30 11:28:26 -07:00
parent 15667435be
commit f814661fa0
28 changed files with 491 additions and 295 deletions

View File

@ -114,7 +114,7 @@ linters-settings:
local-prefixes: github.com/org/project
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
min-complexity: 15
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 20

View File

@ -9,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/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery/cached/disk"
"k8s.io/client-go/dynamic"
@ -38,7 +37,6 @@ type Connection interface {
Config() *Config
DialOrDie() kubernetes.Interface
SwitchContextOrDie(ctx string)
NSDialOrDie() dynamic.NamespaceableResourceInterface
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
RestConfigOrDie() *restclient.Config
MXDial() (*versioned.Clientset, error)
@ -235,23 +233,6 @@ func (a *APIClient) DynDialOrDie() dynamic.Interface {
return a.dClient
}
// NSDialOrDie returns a handle to a namespaced resource.
func (a *APIClient) NSDialOrDie() dynamic.NamespaceableResourceInterface {
a.mx.Lock()
defer a.mx.Unlock()
if a.nsClient != nil {
return a.nsClient
}
a.nsClient = a.DynDialOrDie().Resource(schema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Version: "v1beta1",
Resource: "customresourcedefinitions",
})
return a.nsClient
}
// MXDial returns a handle to the metrics server.
func (a *APIClient) MXDial() (*versioned.Clientset, error) {
a.mx.Lock()

View File

@ -4,6 +4,9 @@
package config_test
import (
"reflect"
"time"
client "github.com/derailed/k9s/internal/client"
pegomock "github.com/petergtz/pegomock"
v1 "k8s.io/api/core/v1"
@ -13,8 +16,6 @@ import (
kubernetes "k8s.io/client-go/kubernetes"
rest "k8s.io/client-go/rest"
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
"reflect"
"time"
)
type MockConnection struct {
@ -202,21 +203,6 @@ func (mock *MockConnection) MXDial() (*versioned.Clientset, error) {
return ret0, ret1
}
func (mock *MockConnection) NSDialOrDie() dynamic.NamespaceableResourceInterface {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockConnection().")
}
params := []pegomock.Param{}
result := pegomock.GetGenericMockFrom(mock).Invoke("NSDialOrDie", params, []reflect.Type{reflect.TypeOf((*dynamic.NamespaceableResourceInterface)(nil)).Elem()})
var ret0 dynamic.NamespaceableResourceInterface
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(dynamic.NamespaceableResourceInterface)
}
}
return ret0
}
func (mock *MockConnection) NodePods(_param0 string) (*v1.PodList, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockConnection().")
@ -570,23 +556,6 @@ func (c *MockConnection_MXDial_OngoingVerification) GetCapturedArguments() {
func (c *MockConnection_MXDial_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockConnection) NSDialOrDie() *MockConnection_NSDialOrDie_OngoingVerification {
params := []pegomock.Param{}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NSDialOrDie", params, verifier.timeout)
return &MockConnection_NSDialOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockConnection_NSDialOrDie_OngoingVerification struct {
mock *MockConnection
methodInvocations []pegomock.MethodInvocation
}
func (c *MockConnection_NSDialOrDie_OngoingVerification) GetCapturedArguments() {
}
func (c *MockConnection_NSDialOrDie_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockConnection) NodePods(_param0 string) *MockConnection_NodePods_OngoingVerification {
params := []pegomock.Param{_param0}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)

View File

@ -176,7 +176,6 @@ func loadPreferred(f Factory, m ResourceMetas) error {
for _, r := range rr {
for _, res := range r.APIResources {
gvr := client.FromGVAndR(r.GroupVersion, res.Name)
log.Debug().Msgf("GVR %s", gvr)
res.Group, res.Version = gvr.ToG(), gvr.ToV()
m[gvr] = res
}
@ -186,12 +185,14 @@ func loadPreferred(f Factory, m ResourceMetas) error {
}
func loadCRDs(f Factory, m ResourceMetas) error {
log.Debug().Msgf("Loading CRDs...")
oo, err := f.List("apiextensions.k8s.io/v1beta1/customresourcedefinitions", "", labels.Everything())
if err != nil {
log.Error().Err(err).Msgf("Fail CRDs load")
return nil
}
f.WaitForCacheSync()
log.Debug().Msgf("CRDS count %d", len(oo))
for _, o := range oo {
meta, errs := extractMeta(o)
@ -200,6 +201,7 @@ func loadCRDs(f Factory, m ResourceMetas) error {
continue
}
gvr := client.NewGVR(meta.Group, meta.Version, meta.Name)
log.Debug().Msgf("CRD %q", gvr)
m[gvr] = meta
}

View File

@ -20,7 +20,7 @@ type Factory interface {
Get(gvr, path string, sel labels.Selector) (runtime.Object, error)
// List fetch a collection of resources.
List(ns, gvr string, sel labels.Selector) ([]runtime.Object, error)
List(gvr, ns string, sel labels.Selector) ([]runtime.Object, error)
// ForResource fetch an informer for a given resource.
ForResource(ns, gvr string) informers.GenericInformer

31
internal/model/crd.go Normal file
View File

@ -0,0 +1,31 @@
package model
import (
"context"
"github.com/derailed/k9s/internal"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
// CustomResourceDefinition represents a CRD resource model.
type CustomResourceDefinition struct {
Resource
}
// List returns a collection of nodes.
func (c *CustomResourceDefinition) List(ctx context.Context) ([]runtime.Object, error) {
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
lsel := labels.Everything()
if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil {
lsel = sel.AsSelector()
}
gvr := "apiextensions.k8s.io/v1beta1/customresourcedefinitions"
oo, err := c.factory.List(gvr, "-", lsel)
if err != nil {
return nil, err
}
return oo, nil
}

View File

@ -3,11 +3,9 @@ package model
import (
"context"
"fmt"
"time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
@ -61,16 +59,11 @@ func (g *Generic) List(ctx context.Context) ([]runtime.Object, error) {
res[i] = RowRes{&g.table.Rows[i]}
}
log.Debug().Msgf("!!!!GENERIC lister returns %d", len(res))
return res, err
}
// Hydrate returns nodes as rows.
func (g *Generic) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
defer func(t time.Time) {
log.Debug().Msgf("HYDRATE elapsed: %v", time.Since(t))
}(time.Now())
gr, ok := re.(*render.Generic)
if !ok {
return fmt.Errorf("expecting generic renderer for %s but got %T", g.gvr, re)

48
internal/model/hpa.go Normal file
View File

@ -0,0 +1,48 @@
package model
import (
"context"
"github.com/derailed/k9s/internal"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
// HorizontalPodAutoscaler represents a HPA resource model.
type HorizontalPodAutoscaler struct {
Resource
}
// List returns a collection of nodes.
func (c *HorizontalPodAutoscaler) List(ctx context.Context) ([]runtime.Object, error) {
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
lsel := labels.Everything()
if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil {
lsel = sel.AsSelector()
}
gvr := "autoscaling/v2beta2/horizontalpodautoscalers"
ooV2b2, err := c.factory.List(gvr, c.namespace, lsel)
if err != nil {
return nil, err
}
if len(ooV2b2) > 0 {
return ooV2b2, nil
}
gvr = "autoscaling/v2beta1/horizontalpodautoscalers"
ooV2b1, err := c.factory.List(gvr, c.namespace, lsel)
if err != nil {
return nil, err
}
if len(ooV2b1) > 0 {
return ooV2b1, nil
}
gvr = "autoscaling/v1/horizontalpodautoscalers"
oo, err := c.factory.List(gvr, c.namespace, lsel)
if err != nil {
return nil, err
}
return oo, nil
}

View File

@ -4,6 +4,9 @@
package model_test
import (
"reflect"
"time"
client "github.com/derailed/k9s/internal/client"
pegomock "github.com/petergtz/pegomock"
v1 "k8s.io/api/core/v1"
@ -13,8 +16,6 @@ import (
kubernetes "k8s.io/client-go/kubernetes"
rest "k8s.io/client-go/rest"
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
"reflect"
"time"
)
type MockClusterMeta struct {
@ -259,21 +260,6 @@ func (mock *MockClusterMeta) MXDial() (*versioned.Clientset, error) {
return ret0, ret1
}
func (mock *MockClusterMeta) NSDialOrDie() dynamic.NamespaceableResourceInterface {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
}
params := []pegomock.Param{}
result := pegomock.GetGenericMockFrom(mock).Invoke("NSDialOrDie", params, []reflect.Type{reflect.TypeOf((*dynamic.NamespaceableResourceInterface)(nil)).Elem()})
var ret0 dynamic.NamespaceableResourceInterface
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(dynamic.NamespaceableResourceInterface)
}
}
return ret0
}
func (mock *MockClusterMeta) NodePods(_param0 string) (*v1.PodList, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
@ -716,23 +702,6 @@ func (c *MockClusterMeta_MXDial_OngoingVerification) GetCapturedArguments() {
func (c *MockClusterMeta_MXDial_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockClusterMeta) NSDialOrDie() *MockClusterMeta_NSDialOrDie_OngoingVerification {
params := []pegomock.Param{}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NSDialOrDie", params, verifier.timeout)
return &MockClusterMeta_NSDialOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockClusterMeta_NSDialOrDie_OngoingVerification struct {
mock *MockClusterMeta
methodInvocations []pegomock.MethodInvocation
}
func (c *MockClusterMeta_NSDialOrDie_OngoingVerification) GetCapturedArguments() {
}
func (c *MockClusterMeta_NSDialOrDie_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockClusterMeta) NodePods(_param0 string) *MockClusterMeta_NodePods_OngoingVerification {
params := []pegomock.Param{_param0}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)

View File

@ -4,6 +4,9 @@
package model_test
import (
"reflect"
"time"
client "github.com/derailed/k9s/internal/client"
pegomock "github.com/petergtz/pegomock"
v1 "k8s.io/api/core/v1"
@ -13,8 +16,6 @@ import (
kubernetes "k8s.io/client-go/kubernetes"
rest "k8s.io/client-go/rest"
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
"reflect"
"time"
)
type MockConnection struct {
@ -202,21 +203,6 @@ func (mock *MockConnection) MXDial() (*versioned.Clientset, error) {
return ret0, ret1
}
func (mock *MockConnection) NSDialOrDie() dynamic.NamespaceableResourceInterface {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockConnection().")
}
params := []pegomock.Param{}
result := pegomock.GetGenericMockFrom(mock).Invoke("NSDialOrDie", params, []reflect.Type{reflect.TypeOf((*dynamic.NamespaceableResourceInterface)(nil)).Elem()})
var ret0 dynamic.NamespaceableResourceInterface
if len(result) != 0 {
if result[0] != nil {
ret0 = result[0].(dynamic.NamespaceableResourceInterface)
}
}
return ret0
}
func (mock *MockConnection) NodePods(_param0 string) (*v1.PodList, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockConnection().")
@ -570,23 +556,6 @@ func (c *MockConnection_MXDial_OngoingVerification) GetCapturedArguments() {
func (c *MockConnection_MXDial_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockConnection) NSDialOrDie() *MockConnection_NSDialOrDie_OngoingVerification {
params := []pegomock.Param{}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NSDialOrDie", params, verifier.timeout)
return &MockConnection_NSDialOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
}
type MockConnection_NSDialOrDie_OngoingVerification struct {
mock *MockConnection
methodInvocations []pegomock.MethodInvocation
}
func (c *MockConnection_NSDialOrDie_OngoingVerification) GetCapturedArguments() {
}
func (c *MockConnection_NSDialOrDie_OngoingVerification) GetAllCapturedArguments() {
}
func (verifier *VerifierMockConnection) NodePods(_param0 string) *MockConnection_NodePods_OngoingVerification {
params := []pegomock.Param{_param0}
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)

View File

@ -115,11 +115,25 @@ var Registry = map[string]ResourceMeta{
// Autoscaling...
"autoscaling/v1/horizontalpodautoscalers": ResourceMeta{
Model: &HorizontalPodAutoscaler{},
Renderer: &render.HorizontalPodAutoscaler{},
},
"autoscaling/v2beta1/horizontalpodautoscalers": ResourceMeta{
Model: &HorizontalPodAutoscaler{},
Renderer: &render.HorizontalPodAutoscaler{},
},
"autoscaling/v2beta2/horizontalpodautoscalers": ResourceMeta{
Model: &HorizontalPodAutoscaler{},
Renderer: &render.HorizontalPodAutoscaler{},
},
// CRDs...
"apiextensions.k8s.io/v1/customresourcedefinitions": ResourceMeta{
Model: &CustomResourceDefinition{},
Renderer: &render.CustomResourceDefinition{},
},
"apiextensions.k8s.io/v1beta1/customresourcedefinitions": ResourceMeta{
Model: &CustomResourceDefinition{},
Renderer: &render.CustomResourceDefinition{},
},

View File

@ -11,7 +11,10 @@ import (
"github.com/rs/zerolog/log"
)
const refreshRate = 1 * time.Second
const (
refreshRate = 1 * time.Second
noDataCount = 2
)
type TableListener interface {
TableDataChanged(render.TableData)
@ -25,6 +28,7 @@ type Table struct {
listeners []TableListener
inUpdate int32
refreshRate time.Duration
zeroCount int32
}
// NewTable returns a new table model.
@ -112,7 +116,6 @@ func (t *Table) refresh(ctx context.Context) {
// AddListener adds a new model listener.
func (t *Table) AddListener(l TableListener) {
t.listeners = append(t.listeners, l)
t.fireTableChanged(*t.data)
}
// RemoveListener delete a listener from the list.
@ -131,6 +134,12 @@ func (t *Table) RemoveListener(l TableListener) {
}
func (t *Table) fireTableChanged(data render.TableData) {
// Needed to wait for the cache to populate but if there is no data at all
// after X ticks need to tell the view no data is present
if len(data.RowEvents) == 0 && atomic.LoadInt32(&t.zeroCount) < noDataCount {
atomic.AddInt32(&t.zeroCount, 1)
return
}
for _, l := range t.listeners {
l.TableDataChanged(data)
}
@ -152,7 +161,7 @@ func (t *Table) reconcile(ctx context.Context) error {
}
m, ok := Registry[string(t.gvr)]
if !ok {
log.Warn().Msgf("Resource %s not found in registry. Going generic!", t.gvr)
log.Debug().Msgf("Resource %s not found in registry. Going generic!", t.gvr)
m = ResourceMeta{
Model: &Generic{},
Renderer: &render.Generic{},

View File

@ -3,9 +3,12 @@ package render
import (
"fmt"
"strconv"
"strings"
"github.com/derailed/tview"
autoscalingv1 "k8s.io/api/autoscaling/v1"
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
@ -28,7 +31,7 @@ func (HorizontalPodAutoscaler) Header(ns string) HeaderRow {
return append(h,
Header{Name: "NAME"},
Header{Name: "REFERENCE"},
Header{Name: "TARGETS"},
Header{Name: "TARGETS%"},
Header{Name: "MINPODS", Align: tview.AlignRight},
Header{Name: "MAXPODS", Align: tview.AlignRight},
Header{Name: "REPLICAS", Align: tview.AlignRight},
@ -43,6 +46,21 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
return fmt.Errorf("Expected HorizontalPodAutoscaler, but got %T", o)
}
v := raw.Object["apiVersion"]
switch v {
case "autoscaling/v1":
return h.renderV1(raw, ns, r)
case "autoscaling/v2beta1":
return h.renderV2b1(raw, ns, r)
case "autoscaling/v2beta2":
return h.renderV2b2(raw, ns, r)
default:
return fmt.Errorf("Unhandled HPA version %q", v)
}
}
func (h HorizontalPodAutoscaler) renderV1(raw *unstructured.Unstructured, ns string, r *Row) error {
var hpa autoscalingv1.HorizontalPodAutoscaler
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
if err != nil {
@ -57,7 +75,59 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
r.Fields = append(r.Fields,
hpa.ObjectMeta.Name,
hpa.Spec.ScaleTargetRef.Name,
toMetrics(hpa.Spec, hpa.Status),
toMetricsV1(hpa.Spec, hpa.Status),
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
toAge(hpa.ObjectMeta.CreationTimestamp),
)
return nil
}
func (h HorizontalPodAutoscaler) renderV2b1(raw *unstructured.Unstructured, ns string, r *Row) error {
var hpa autoscalingv2beta1.HorizontalPodAutoscaler
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
if err != nil {
return err
}
r.ID = MetaFQN(hpa.ObjectMeta)
r.Fields = make(Fields, 0, len(h.Header(ns)))
if isAllNamespace(ns) {
r.Fields = append(r.Fields, hpa.Namespace)
}
r.Fields = append(r.Fields,
hpa.ObjectMeta.Name,
hpa.Spec.ScaleTargetRef.Name,
toMetricsV2b1(hpa.Spec.Metrics, hpa.Status.CurrentMetrics),
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
toAge(hpa.ObjectMeta.CreationTimestamp),
)
return nil
}
func (h HorizontalPodAutoscaler) renderV2b2(raw *unstructured.Unstructured, ns string, r *Row) error {
var hpa autoscalingv2beta2.HorizontalPodAutoscaler
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
if err != nil {
return err
}
r.ID = MetaFQN(hpa.ObjectMeta)
r.Fields = make(Fields, 0, len(h.Header(ns)))
if isAllNamespace(ns) {
r.Fields = append(r.Fields, hpa.Namespace)
}
r.Fields = append(r.Fields,
hpa.ObjectMeta.Name,
hpa.Spec.ScaleTargetRef.Name,
toMetricsV2b2(hpa.Spec.Metrics, hpa.Status.CurrentMetrics),
strconv.Itoa(int(*hpa.Spec.MinReplicas)),
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
@ -70,7 +140,7 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
// ----------------------------------------------------------------------------
// Helpers...
func toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
func toMetricsV1(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
current := "<unknown>"
if status.CurrentCPUUtilizationPercentage != nil {
current = strconv.Itoa(int(*status.CurrentCPUUtilizationPercentage)) + "%"
@ -82,3 +152,176 @@ func toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalin
}
return current + "/" + target + "%"
}
func toMetricsV2b1(specs []autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
if len(specs) == 0 {
return MissingValue
}
list, count := []string{}, 0
for i, spec := range specs {
list = append(list, checkHPAType(i, spec, statuses))
count++
}
max, more := 2, false
if count > max {
list, more = list[:max], true
}
ret := strings.Join(list, ", ")
if more {
return ret + " + " + strconv.Itoa(count-max) + "more..."
}
return ret
}
func toMetricsV2b2(specs []autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
if len(specs) == 0 {
return MissingValue
}
list, max, more, count := []string{}, 2, false, 0
for i, spec := range specs {
current := "<unknown>"
switch spec.Type {
case autoscalingv2beta2.ExternalMetricSourceType:
list = append(list, externalMetricsV2b2(i, spec, statuses))
case autoscalingv2beta2.PodsMetricSourceType:
if len(statuses) > i && statuses[i].Pods != nil {
current = statuses[i].Pods.Current.AverageValue.String()
}
list = append(list, current+"/"+spec.Pods.Target.AverageValue.String())
case autoscalingv2beta2.ObjectMetricSourceType:
if len(statuses) > i && statuses[i].Object != nil {
current = statuses[i].Object.Current.Value.String()
}
list = append(list, current+"/"+spec.Object.Target.Value.String())
case autoscalingv2beta2.ResourceMetricSourceType:
list = append(list, resourceMetricsV2b2(i, spec, statuses))
default:
list = append(list, "<unknown type>")
}
count++
}
if count > max {
list, more = list[:max], true
}
ret := strings.Join(list, ", ")
if more {
return ret + " + " + strconv.Itoa(count-max) + "more..."
}
return ret
}
func checkHPAType(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
current := "<unknown>"
switch spec.Type {
case autoscalingv2beta1.ExternalMetricSourceType:
return externalMetricsV2b1(i, spec, statuses)
case autoscalingv2beta1.PodsMetricSourceType:
if len(statuses) > i && statuses[i].Pods != nil {
current = statuses[i].Pods.CurrentAverageValue.String()
}
return current + "/" + spec.Pods.TargetAverageValue.String()
case autoscalingv2beta1.ObjectMetricSourceType:
if len(statuses) > i && statuses[i].Object != nil {
current = statuses[i].Object.CurrentValue.String()
}
return current + "/" + spec.Object.TargetValue.String()
case autoscalingv2beta1.ResourceMetricSourceType:
return resourceMetricsV2b1(i, spec, statuses)
}
return "<unknown type>"
}
func externalMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
current := "<unknown>"
if spec.External.Target.AverageValue != nil {
if len(statuses) > i && statuses[i].External != nil && &statuses[i].External.Current.AverageValue != nil {
current = statuses[i].External.Current.AverageValue.String()
}
return current + "/" + spec.External.Target.AverageValue.String() + " (avg)"
}
if len(statuses) > i && statuses[i].External != nil {
current = statuses[i].External.Current.Value.String()
}
return current + "/" + spec.External.Target.Value.String()
}
func resourceMetricsV2b2(i int, spec autoscalingv2beta2.MetricSpec, statuses []autoscalingv2beta2.MetricStatus) string {
current := "<unknown>"
if spec.Resource.Target.AverageValue != nil {
if len(statuses) > i && statuses[i].Resource != nil {
current = statuses[i].Resource.Current.AverageValue.String()
}
return current + "/" + spec.Resource.Target.AverageValue.String()
}
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.Current.AverageUtilization != nil {
current = AsPerc(float64(*statuses[i].Resource.Current.AverageUtilization))
}
target := "<auto>"
if spec.Resource.Target.AverageUtilization != nil {
target = AsPerc(float64(*spec.Resource.Target.AverageUtilization))
}
return current + "/" + target
}
func externalMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
current := "<unknown>"
if spec.External.TargetAverageValue != nil {
if len(statuses) > i && statuses[i].External != nil && &statuses[i].External.CurrentAverageValue != nil {
current = statuses[i].External.CurrentAverageValue.String()
}
return current + "/" + spec.External.TargetAverageValue.String() + " (avg)"
}
if len(statuses) > i && statuses[i].External != nil {
current = statuses[i].External.CurrentValue.String()
}
return current + "/" + spec.External.TargetValue.String()
}
func resourceMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
current := "<unknown>"
if status := checkTargetMetricsV2b1(i, spec, statuses); status != "" {
return status
}
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
current = AsPerc(float64(*statuses[i].Resource.CurrentAverageUtilization))
}
target := "<auto>"
if spec.Resource.TargetAverageUtilization != nil {
target = AsPerc(float64(*spec.Resource.TargetAverageUtilization))
}
return current + "/" + target
}
func checkTargetMetricsV2b1(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
if spec.Resource.TargetAverageValue == nil {
return ""
}
var current string
if len(statuses) > i && statuses[i].Resource != nil {
current = statuses[i].Resource.CurrentAverageValue.String()
}
return current + "/" + spec.Resource.TargetAverageValue.String()
}

View File

@ -178,12 +178,6 @@ func (a *App) redrawCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt
}
// StatusReset reset log back to normal.
func (a *App) StatusReset() {
a.Logo().Reset()
a.Draw()
}
// View Accessora...
// Crumbs return app crumba.

View File

@ -6,7 +6,6 @@ import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const defaultPrompt = "%c> %s"
@ -86,7 +85,6 @@ func (c *Command) BufferActive(f bool, k BufferKind) {
c.SetBackgroundColor(c.styles.BgColor())
c.Clear()
}
log.Debug().Msgf("Command activated: %t", c.activated)
}
func colorFor(k BufferKind) tcell.Color {

View File

@ -152,6 +152,7 @@ func styleTitle(rc int, ns, base, path, buff string, styles *config.Styles) stri
rc--
}
base = strings.Title(base)
if ns == render.AllNamespaces {
ns = render.NamespaceAll
}

View File

@ -338,13 +338,19 @@ func (a *App) Run() {
}
}
func (a *App) status(l ui.FlashLevel, msg string) {
func (a *App) Status(l ui.FlashLevel, msg string) {
a.Flash().Info(msg)
a.setIndicator(l, msg)
a.setLogo(l, msg)
a.Draw()
}
// StatusReset reset log back to normal.
func (a *App) ClearStatus() {
a.Logo().Reset()
a.Draw()
}
func (a *App) setLogo(l ui.FlashLevel, msg string) {
switch l {
case ui.FlashErr:

View File

@ -13,23 +13,17 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const resultTitle = "Benchmark Results"
// Benchmark represents a service benchmark results view.
type Benchmark struct {
ResourceViewer
details *Details
}
// NewBench returns a new viewer.
func NewBenchmark(gvr client.GVR) ResourceViewer {
b := Benchmark{
ResourceViewer: NewBrowser(gvr),
details: NewDetails(resultTitle),
}
b.GetTable().SetBorderFocusColor(tcell.ColorSeaGreen)
b.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorSeaGreen, tcell.AttrNone)
@ -46,27 +40,18 @@ func (b *Benchmark) benchContext(ctx context.Context) context.Context {
}
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 {
app.Flash().Errf("Unable to load bench file %s", err)
return
}
b.details.SetText(data)
b.details.SetSubject(fileToSubject(path))
if err := app.inject(b.details); err != nil {
details := NewDetails(b.App(), "Benchmark", fileToSubject(path)).Update(data)
if err := app.inject(details); err != nil {
app.Flash().Err(err)
}
}
func fileToSubject(path string) string {
tokens := strings.Split(path, "/")
log.Debug().Msgf("TOKENS %v", tokens)
ee := strings.Split(tokens[len(tokens)-1], "_")
return ee[0] + "/" + ee[1]
}
func (b *Benchmark) benchFile() string {
r := b.GetTable().GetSelectedRowIndex()
return ui.TrimCell(b.GetTable().SelectTable, r, 7)
@ -75,6 +60,12 @@ func (b *Benchmark) benchFile() string {
// ----------------------------------------------------------------------------
// Helpers...
func fileToSubject(path string) string {
tokens := strings.Split(path, "/")
ee := strings.Split(tokens[len(tokens)-1], "_")
return ee[0] + "/" + ee[1]
}
func benchDir(cfg *config.Config) string {
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster)
}

View File

@ -34,9 +34,9 @@ type Browser struct {
namespaces map[int]string
gvr client.GVR
envFn EnvFunc
meta metav1.APIResource
accessor dao.Accessor
envFn EnvFunc
contextFn ContextFunc
bindKeysFn BindKeysFunc
cancelFn context.CancelFunc
@ -72,7 +72,6 @@ func (b *Browser) Init(ctx context.Context) error {
b.bindKeysFn(b.Actions())
}
b.BaseTitle = b.meta.Kind
b.SetTitle(" [orange:i:]LOADING... ")
b.accessor, err = dao.AccessorFor(b.app.factory, b.gvr)
if err != nil {
return err
@ -85,6 +84,7 @@ func (b *Browser) Init(ctx context.Context) error {
b.Select(1, 0)
}
b.GetModel().AddListener(b)
b.App().Status(ui.FlashWarn, "Loading...")
return nil
}
@ -181,6 +181,23 @@ func (b *Browser) GVR() string { return string(b.gvr) }
// GetTable returns the underlying table.
func (b *Browser) GetTable() *Table { return b.Table }
// TableLoadChanged notifies view something went south.
func (b *Browser) TableLoadFailed(err error) {
b.app.QueueUpdateDraw(func() {
b.app.Flash().Err(err)
b.App().ClearStatus()
})
}
// TableDataChanged notifies view new data is available.
func (b *Browser) TableDataChanged(data render.TableData) {
b.app.QueueUpdateDraw(func() {
b.refreshActions()
b.Update(data)
b.App().ClearStatus()
})
}
// ----------------------------------------------------------------------------
// Actions()...
@ -218,6 +235,7 @@ func (b *Browser) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
func (b *Browser) refreshCmd(*tcell.EventKey) *tcell.EventKey {
b.app.Flash().Info("Refreshing...")
b.refresh()
return nil
}
@ -226,7 +244,6 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
if len(selections) == 0 {
return evt
}
log.Debug().Msgf("DEL SELECTIONS %#v", selections)
b.Stop()
defer b.Start()
@ -235,52 +252,57 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
if len(selections) > 1 {
msg = fmt.Sprintf("Delete %d marked %s?", len(selections), b.gvr)
}
cancelFn := func() {}
if dao.IsK9sMeta(b.meta) {
dialog.ShowConfirm(b.app.Content.Pages, "Confirm Delete", msg, func() {
b.ShowDeleted()
if len(selections) > 1 {
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
} else {
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
}
for _, sel := range selections {
if err := b.accessor.(dao.Nuker).Delete(sel, true, true); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err)
} else {
b.GetTable().DeleteMark(sel)
}
}
b.refresh()
b.SelectRow(1, true)
}, cancelFn)
b.simpleDelete(selections, msg)
return nil
}
dialog.ShowDelete(b.app.Content.Pages, msg, func(cascade, force bool) {
b.ShowDeleted()
if len(selections) > 1 {
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
} else {
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
}
for _, sel := range selections {
if err := b.accessor.(dao.Nuker).Delete(sel, cascade, force); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err)
} else {
b.app.factory.DeleteForwarder(sel)
b.GetTable().DeleteMark(sel)
}
}
b.refresh()
b.SelectRow(1, true)
}, cancelFn)
b.resourceDelete(selections, msg)
}
return nil
}
func (b *Browser) simpleDelete(selections []string, msg string) {
dialog.ShowConfirm(b.app.Content.Pages, "Confirm Delete", msg, func() {
b.ShowDeleted()
if len(selections) > 1 {
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
} else {
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
}
for _, sel := range selections {
if err := b.accessor.(dao.Nuker).Delete(sel, true, true); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err)
} else {
b.GetTable().DeleteMark(sel)
}
}
b.refresh()
b.SelectRow(1, true)
}, func() {})
}
func (b *Browser) resourceDelete(selections []string, msg string) {
dialog.ShowDelete(b.app.Content.Pages, msg, func(cascade, force bool) {
b.ShowDeleted()
if len(selections) > 1 {
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.gvr)
} else {
b.app.Flash().Infof("Delete resource %s %s", b.gvr, selections[0])
}
for _, sel := range selections {
if err := b.accessor.(dao.Nuker).Delete(sel, cascade, force); err != nil {
b.app.Flash().Errf("Delete failed with `%s", err)
} else {
b.app.factory.DeleteForwarder(sel)
b.GetTable().DeleteMark(sel)
}
}
b.refresh()
b.SelectRow(1, true)
}, func() {})
}
func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
path := b.GetSelectedItem()
if path == "" {
@ -299,18 +321,7 @@ func (b *Browser) describeResource(app *App, _, _, sel string) {
return
}
details := NewDetails("Describe")
ctx := context.WithValue(context.Background(), internal.KeyApp, b.App())
if err := details.Init(ctx); err != nil {
log.Error().Err(err).Msg("Details init failed")
return
}
details.SetSubject(sel)
details.SetTextColor(b.app.Styles.FgColor())
details.Update(yaml)
// BOZO!!
// details.SetText(colorizeYAML(b.app.Styles.Views().Yaml, yaml))
// details.ScrollToBeginning()
details := NewDetails(b.App(), "Describe", sel).Update(yaml)
if err := b.app.inject(details); err != nil {
b.app.Flash().Err(err)
}
@ -322,7 +333,6 @@ func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt
}
log.Debug().Msgf("------ NAMESPACES %q vs %q", path, b.GetModel().GetNamespace())
o, err := b.app.factory.Get(string(b.gvr), path, labels.Everything())
if err != nil {
b.app.Flash().Errf("Unable to get resource %q -- %s", b.gvr, err)
@ -335,11 +345,7 @@ func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
details := NewDetails("YAML")
details.SetSubject(path)
details.SetTextColor(b.app.Styles.FgColor())
details.SetText(colorizeYAML(b.app.Styles.Views().Yaml, raw))
details.ScrollToBeginning()
details := NewDetails(b.app, "YAML", path).Update(raw)
if err := b.app.inject(details); err != nil {
b.App().Flash().Err(err)
}
@ -399,7 +405,6 @@ func (b *Browser) setNamespace(ns string) {
if ns == render.NamespaceAll {
ns = render.AllNamespaces
}
log.Debug().Msgf("!!!!!! SETTING NS %q", ns)
b.GetModel().SetNamespace(ns)
}
@ -427,21 +432,6 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
// TableLoadChanged notifies view something went south.
func (b *Browser) TableLoadFailed(err error) {
b.app.QueueUpdateDraw(func() {
b.app.Flash().Err(err)
})
}
// TableDataChanged notifies view new data is available.
func (b *Browser) TableDataChanged(data render.TableData) {
b.app.QueueUpdateDraw(func() {
b.refreshActions()
b.Update(data)
})
}
func (b *Browser) defaultContext() context.Context {
ctx := context.Background()

View File

@ -25,21 +25,20 @@ type Details struct {
}
// NewDetails returns a details viewer.
func NewDetails(title string) *Details {
return &Details{
func NewDetails(app *App, title, subject string) *Details {
d := Details{
TextView: tview.NewTextView(),
app: app,
title: title,
subject: subject,
actions: make(ui.KeyActions),
}
return &d
}
// Init initializes the viewer.
func (d *Details) Init(ctx context.Context) error {
var err error
if d.app, err = extractApp(ctx); err != nil {
return err
}
func (d *Details) Init(_ context.Context) error {
if d.title != "" {
d.SetBorder(true)
}
@ -68,10 +67,12 @@ func (d *Details) StylesChanged(s *config.Styles) {
d.Update(d.buff)
}
func (d *Details) Update(buff string) {
func (d *Details) Update(buff string) *Details {
d.buff = buff
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, buff))
d.ScrollToBeginning()
return d
}
func (d *Details) Actions() ui.KeyActions {
@ -107,10 +108,10 @@ func (d *Details) keyboard(evt *tcell.EventKey) *tcell.EventKey {
if key == tcell.KeyRune {
key = tcell.Key(evt.Rune())
}
if a, ok := d.actions[key]; ok {
return a.Action(evt)
}
return evt
}

View File

@ -71,16 +71,15 @@ func (l *Log) Init(ctx context.Context) (err error) {
l.indicator = NewLogIndicator(l.app.Styles)
l.AddItem(l.indicator, 1, 1, false)
l.logs = NewDetails("")
l.logs.SetBorder(false)
l.logs.SetDynamicColors(true)
l.logs.SetTextColor(config.AsColor(l.app.Styles.Views().Log.FgColor))
l.logs.SetBackgroundColor(config.AsColor(l.app.Styles.Views().Log.BgColor))
l.logs.SetWrap(true)
l.logs.SetMaxBuffer(l.app.Config.K9s.LogBufferSize)
l.logs = NewDetails(l.app, "", "")
if err = l.logs.Init(ctx); err != nil {
return err
}
l.logs.SetWrap(false)
l.logs.SetMaxBuffer(l.app.Config.K9s.LogBufferSize)
l.logs.SetTextColor(config.AsColor(l.app.Styles.Views().Log.FgColor))
l.logs.SetBackgroundColor(config.AsColor(l.app.Styles.Views().Log.BgColor))
l.ansiWriter = tview.ANSIWriter(l.logs, l.app.Styles.Views().Log.FgColor, l.app.Styles.Views().Log.BgColor)
l.AddItem(l.logs, 0, 1, true)
l.bindKeys()

View File

@ -4,7 +4,6 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/ui"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -46,8 +45,8 @@ func (n *Node) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
}
sel := n.GetTable().GetSelectedItem()
log.Debug().Msgf("------ VIEW NODE %q", sel)
o, err := n.App().factory.Client().DynDialOrDie().Resource(client.GVR(n.GVR()).AsGVR()).Get(sel, metav1.GetOptions{})
gvr := client.GVR(n.GVR()).AsGVR()
o, err := n.App().factory.Client().DynDialOrDie().Resource(gvr).Get(sel, metav1.GetOptions{})
if err != nil {
n.App().Flash().Errf("Unable to get resource %q -- %s", n.GVR(), err)
return nil
@ -59,11 +58,7 @@ func (n *Node) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
details := NewDetails("YAML")
details.SetSubject(sel)
details.SetTextColor(n.App().Styles.FgColor())
details.SetText(colorizeYAML(n.App().Styles.Views().Yaml, raw))
details.ScrollToBeginning()
details := NewDetails(n.App(), "YAML", sel).Update(raw)
if err := n.App().inject(details); err != nil {
n.App().Flash().Err(err)
}

View File

@ -67,10 +67,10 @@ func (p *PortForward) showBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
func (p *PortForward) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
if p.bench != nil {
log.Debug().Msg(">>> Benchmark cancelFned!!")
p.App().status(ui.FlashErr, "Benchmark Camceled!")
p.App().Status(ui.FlashErr, "Benchmark Camceled!")
p.bench.Cancel()
}
p.App().StatusReset()
p.App().ClearStatus()
return nil
}
@ -97,11 +97,11 @@ func (p *PortForward) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
var err error
if p.bench, err = perf.NewBenchmark(base, cfg); err != nil {
p.App().Flash().Errf("Bench failed %v", err)
p.App().StatusReset()
p.App().ClearStatus()
return nil
}
p.App().status(ui.FlashWarn, "Benchmark in progress...")
p.App().Status(ui.FlashWarn, "Benchmark in progress...")
log.Debug().Msg("Bench starting...")
go p.runBenchmark()
@ -113,15 +113,15 @@ func (p *PortForward) runBenchmark() {
log.Debug().Msg("Bench Completed!")
p.App().QueueUpdate(func() {
if p.bench.Canceled() {
p.App().status(ui.FlashInfo, "Benchmark cancelFned")
p.App().Status(ui.FlashInfo, "Benchmark canceled")
} else {
p.App().status(ui.FlashInfo, "Benchmark Completed!")
p.App().Status(ui.FlashInfo, "Benchmark Completed!")
p.bench.Cancel()
}
p.bench = nil
go func() {
<-time.After(2 * time.Second)
p.App().QueueUpdate(func() { p.App().StatusReset() })
p.App().QueueUpdate(func() { p.App().ClearStatus() })
}()
})
})

View File

@ -62,11 +62,7 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
details := NewDetails("Decoder")
details.SetSubject(path)
details.SetTextColor(s.App().Styles.FgColor())
details.SetText(colorizeYAML(s.App().Styles.Views().Yaml, string(raw)))
details.ScrollToBeginning()
details := NewDetails(s.App(), "Secret Decoder", path).Update(string(raw))
if err := s.App().inject(details); err != nil {
s.App().Flash().Err(err)
}

View File

@ -67,10 +67,10 @@ func (s *Service) showPods(app *App, ns, gvr, path string) {
func (s *Service) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
if s.bench != nil {
log.Debug().Msg(">>> Benchmark canceled!!")
s.App().status(ui.FlashErr, "Benchmark Canceled!")
s.App().Status(ui.FlashErr, "Benchmark Canceled!")
s.bench.Cancel()
}
s.App().StatusReset()
s.App().ClearStatus()
return nil
}
@ -136,13 +136,14 @@ func (s *Service) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if err := s.runBenchmark(port, cfg); err != nil {
s.App().Flash().Errf("Benchmark failed %v", err)
s.App().StatusReset()
s.App().ClearStatus()
s.bench = nil
}
return nil
}
// BOZO!! Refactor used by forwards
func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
if cfg.HTTP.Host == "" {
return fmt.Errorf("Invalid benchmark host %q", cfg.HTTP.Host)
@ -154,7 +155,7 @@ func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
return err
}
s.App().status(ui.FlashWarn, "Benchmark in progress...")
s.App().Status(ui.FlashWarn, "Benchmark in progress...")
log.Debug().Msg("Bench starting...")
go s.bench.Run(s.App().Config.K9s.CurrentCluster, s.benchDone)
@ -165,9 +166,9 @@ func (s *Service) benchDone() {
log.Debug().Msg("Bench Completed!")
s.App().QueueUpdate(func() {
if s.bench.Canceled() {
s.App().status(ui.FlashInfo, "Benchmark canceled")
s.App().Status(ui.FlashInfo, "Benchmark canceled")
} else {
s.App().status(ui.FlashInfo, "Benchmark Completed!")
s.App().Status(ui.FlashInfo, "Benchmark Completed!")
s.bench.Cancel()
}
s.bench = nil
@ -178,6 +179,6 @@ func (s *Service) benchDone() {
func benchTimedOut(app *App) {
<-time.After(2 * time.Second)
app.QueueUpdate(func() {
app.StatusReset()
app.ClearStatus()
})
}

View File

@ -31,7 +31,6 @@ func (t *Table) Init(ctx context.Context) (err error) {
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
t.Table.Init(ctx)
t.bindKeys()
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
return nil

View File

@ -74,7 +74,10 @@ func (f *Factory) Get(gvr, path string, sel labels.Selector) (runtime.Object, er
// WaitForCachesync waits for all factories to update their cache.
func (f *Factory) WaitForCacheSync() {
for _, fac := range f.factories {
fac.WaitForCacheSync(f.stopChan)
m := fac.WaitForCacheSync(f.stopChan)
for k, v := range m {
log.Debug().Msgf("CACHE -- Loaded %q:%v", k, v)
}
}
}
@ -98,12 +101,10 @@ func (f *Factory) Terminate() {
// RegisterForwarder registers a new portforward for a given container.
func (f *Factory) AddForwarder(pf Forwarder) {
f.forwarders[pf.Path()] = pf
f.forwarders.Dump()
}
// DeleteForwarder deletes portforward for a given container.
func (f *Factory) DeleteForwarder(path string) {
f.forwarders.Dump()
fwd, ok := f.forwarders[path]
if !ok {
log.Warn().Msgf("Unable to delete portForward %q", path)
@ -111,7 +112,6 @@ func (f *Factory) DeleteForwarder(path string) {
}
fwd.Stop()
delete(f.forwarders, path)
f.forwarders.Dump()
}
// Forwards returns all portforwards.
@ -146,10 +146,11 @@ func (f *Factory) isClusterWide() bool {
return ok
}
func (f *Factory) preload(ns string) {
// verbs := []string{"get", "list", "watch"}
func (f *Factory) preload(_ string) {
// BOZO!!
verbs := []string{"get", "list", "watch"}
// _, _ = f.CanForResource(ns, "v1/pods", verbs...)
// _, _ = f.CanForResource(allNamespaces, "apiextensions.k8s.io/v1beta1/customresourcedefinitions", verbs...)
_, _ = f.CanForResource("", "apiextensions.k8s.io/v1beta1/customresourcedefinitions", verbs...)
// _, _ = f.CanForResource(clusterScope, "rbac.authorization.k8s.io/v1/clusterroles", verbs...)
// _, _ = f.CanForResource(allNamespaces, "rbac.authorization.k8s.io/v1/roles", verbs...)
}

View File

@ -41,7 +41,6 @@ func NewForwarders() Forwarders {
// KillAll stops and delete all port-forwards.
func (ff Forwarders) DeleteAll() {
ff.Dump()
for k, f := range ff {
log.Debug().Msgf("Deleting forwarder %s", f.Path())
f.Stop()
@ -51,9 +50,6 @@ func (ff Forwarders) DeleteAll() {
// Kill stops and delete a port-forwards associated with pod.
func (ff Forwarders) Kill(path string) int {
ff.Dump()
log.Debug().Msgf("Delete port-forward %q", path)
hasContainer := strings.Contains(path, ":")
var stats int
for k, f := range ff {
@ -63,7 +59,7 @@ func (ff Forwarders) Kill(path string) int {
}
if victim == path {
stats++
log.Debug().Msgf("Stopping and delete port-forward %s", k)
log.Debug().Msgf("Stop + Delete port-forward %s", k)
f.Stop()
delete(ff, k)
}