fix hpas + cleanup
parent
15667435be
commit
f814661fa0
|
|
@ -114,7 +114,7 @@ linters-settings:
|
||||||
local-prefixes: github.com/org/project
|
local-prefixes: github.com/org/project
|
||||||
gocyclo:
|
gocyclo:
|
||||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||||
min-complexity: 10
|
min-complexity: 15
|
||||||
gocognit:
|
gocognit:
|
||||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||||
min-complexity: 20
|
min-complexity: 20
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
authorizationv1 "k8s.io/api/authorization/v1"
|
authorizationv1 "k8s.io/api/authorization/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
"k8s.io/client-go/discovery/cached/disk"
|
"k8s.io/client-go/discovery/cached/disk"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
|
@ -38,7 +37,6 @@ type Connection interface {
|
||||||
Config() *Config
|
Config() *Config
|
||||||
DialOrDie() kubernetes.Interface
|
DialOrDie() kubernetes.Interface
|
||||||
SwitchContextOrDie(ctx string)
|
SwitchContextOrDie(ctx string)
|
||||||
NSDialOrDie() dynamic.NamespaceableResourceInterface
|
|
||||||
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||||
RestConfigOrDie() *restclient.Config
|
RestConfigOrDie() *restclient.Config
|
||||||
MXDial() (*versioned.Clientset, error)
|
MXDial() (*versioned.Clientset, error)
|
||||||
|
|
@ -235,23 +233,6 @@ func (a *APIClient) DynDialOrDie() dynamic.Interface {
|
||||||
return a.dClient
|
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.
|
// MXDial returns a handle to the metrics server.
|
||||||
func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
||||||
a.mx.Lock()
|
a.mx.Lock()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
package config_test
|
package config_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
client "github.com/derailed/k9s/internal/client"
|
client "github.com/derailed/k9s/internal/client"
|
||||||
pegomock "github.com/petergtz/pegomock"
|
pegomock "github.com/petergtz/pegomock"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
@ -13,8 +16,6 @@ import (
|
||||||
kubernetes "k8s.io/client-go/kubernetes"
|
kubernetes "k8s.io/client-go/kubernetes"
|
||||||
rest "k8s.io/client-go/rest"
|
rest "k8s.io/client-go/rest"
|
||||||
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockConnection struct {
|
type MockConnection struct {
|
||||||
|
|
@ -202,21 +203,6 @@ func (mock *MockConnection) MXDial() (*versioned.Clientset, error) {
|
||||||
return ret0, ret1
|
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) {
|
func (mock *MockConnection) NodePods(_param0 string) (*v1.PodList, error) {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
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 (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 {
|
func (verifier *VerifierMockConnection) NodePods(_param0 string) *MockConnection_NodePods_OngoingVerification {
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,6 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
for _, r := range rr {
|
for _, r := range rr {
|
||||||
for _, res := range r.APIResources {
|
for _, res := range r.APIResources {
|
||||||
gvr := client.FromGVAndR(r.GroupVersion, res.Name)
|
gvr := client.FromGVAndR(r.GroupVersion, res.Name)
|
||||||
log.Debug().Msgf("GVR %s", gvr)
|
|
||||||
res.Group, res.Version = gvr.ToG(), gvr.ToV()
|
res.Group, res.Version = gvr.ToG(), gvr.ToV()
|
||||||
m[gvr] = res
|
m[gvr] = res
|
||||||
}
|
}
|
||||||
|
|
@ -186,12 +185,14 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadCRDs(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())
|
oo, err := f.List("apiextensions.k8s.io/v1beta1/customresourcedefinitions", "", labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fail CRDs load")
|
log.Error().Err(err).Msgf("Fail CRDs load")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f.WaitForCacheSync()
|
f.WaitForCacheSync()
|
||||||
|
log.Debug().Msgf("CRDS count %d", len(oo))
|
||||||
|
|
||||||
for _, o := range oo {
|
for _, o := range oo {
|
||||||
meta, errs := extractMeta(o)
|
meta, errs := extractMeta(o)
|
||||||
|
|
@ -200,6 +201,7 @@ func loadCRDs(f Factory, m ResourceMetas) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gvr := client.NewGVR(meta.Group, meta.Version, meta.Name)
|
gvr := client.NewGVR(meta.Group, meta.Version, meta.Name)
|
||||||
|
log.Debug().Msgf("CRD %q", gvr)
|
||||||
m[gvr] = meta
|
m[gvr] = meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type Factory interface {
|
||||||
Get(gvr, path string, sel labels.Selector) (runtime.Object, error)
|
Get(gvr, path string, sel labels.Selector) (runtime.Object, error)
|
||||||
|
|
||||||
// List fetch a collection of resources.
|
// 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 fetch an informer for a given resource.
|
||||||
ForResource(ns, gvr string) informers.GenericInformer
|
ForResource(ns, gvr string) informers.GenericInformer
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,9 @@ package model
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"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]}
|
res[i] = RowRes{&g.table.Rows[i]}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("!!!!GENERIC lister returns %d", len(res))
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hydrate returns nodes as rows.
|
// Hydrate returns nodes as rows.
|
||||||
func (g *Generic) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
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)
|
gr, ok := re.(*render.Generic)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("expecting generic renderer for %s but got %T", g.gvr, re)
|
return fmt.Errorf("expecting generic renderer for %s but got %T", g.gvr, re)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
package model_test
|
package model_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
client "github.com/derailed/k9s/internal/client"
|
client "github.com/derailed/k9s/internal/client"
|
||||||
pegomock "github.com/petergtz/pegomock"
|
pegomock "github.com/petergtz/pegomock"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
@ -13,8 +16,6 @@ import (
|
||||||
kubernetes "k8s.io/client-go/kubernetes"
|
kubernetes "k8s.io/client-go/kubernetes"
|
||||||
rest "k8s.io/client-go/rest"
|
rest "k8s.io/client-go/rest"
|
||||||
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockClusterMeta struct {
|
type MockClusterMeta struct {
|
||||||
|
|
@ -259,21 +260,6 @@ func (mock *MockClusterMeta) MXDial() (*versioned.Clientset, error) {
|
||||||
return ret0, ret1
|
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) {
|
func (mock *MockClusterMeta) NodePods(_param0 string) (*v1.PodList, error) {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
|
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 (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 {
|
func (verifier *VerifierMockClusterMeta) NodePods(_param0 string) *MockClusterMeta_NodePods_OngoingVerification {
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
package model_test
|
package model_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
client "github.com/derailed/k9s/internal/client"
|
client "github.com/derailed/k9s/internal/client"
|
||||||
pegomock "github.com/petergtz/pegomock"
|
pegomock "github.com/petergtz/pegomock"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
@ -13,8 +16,6 @@ import (
|
||||||
kubernetes "k8s.io/client-go/kubernetes"
|
kubernetes "k8s.io/client-go/kubernetes"
|
||||||
rest "k8s.io/client-go/rest"
|
rest "k8s.io/client-go/rest"
|
||||||
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockConnection struct {
|
type MockConnection struct {
|
||||||
|
|
@ -202,21 +203,6 @@ func (mock *MockConnection) MXDial() (*versioned.Clientset, error) {
|
||||||
return ret0, ret1
|
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) {
|
func (mock *MockConnection) NodePods(_param0 string) (*v1.PodList, error) {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
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 (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 {
|
func (verifier *VerifierMockConnection) NodePods(_param0 string) *MockConnection_NodePods_OngoingVerification {
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NodePods", params, verifier.timeout)
|
||||||
|
|
|
||||||
|
|
@ -115,11 +115,25 @@ var Registry = map[string]ResourceMeta{
|
||||||
|
|
||||||
// Autoscaling...
|
// Autoscaling...
|
||||||
"autoscaling/v1/horizontalpodautoscalers": ResourceMeta{
|
"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{},
|
Renderer: &render.HorizontalPodAutoscaler{},
|
||||||
},
|
},
|
||||||
|
|
||||||
// CRDs...
|
// CRDs...
|
||||||
|
"apiextensions.k8s.io/v1/customresourcedefinitions": ResourceMeta{
|
||||||
|
Model: &CustomResourceDefinition{},
|
||||||
|
Renderer: &render.CustomResourceDefinition{},
|
||||||
|
},
|
||||||
"apiextensions.k8s.io/v1beta1/customresourcedefinitions": ResourceMeta{
|
"apiextensions.k8s.io/v1beta1/customresourcedefinitions": ResourceMeta{
|
||||||
|
Model: &CustomResourceDefinition{},
|
||||||
Renderer: &render.CustomResourceDefinition{},
|
Renderer: &render.CustomResourceDefinition{},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const refreshRate = 1 * time.Second
|
const (
|
||||||
|
refreshRate = 1 * time.Second
|
||||||
|
noDataCount = 2
|
||||||
|
)
|
||||||
|
|
||||||
type TableListener interface {
|
type TableListener interface {
|
||||||
TableDataChanged(render.TableData)
|
TableDataChanged(render.TableData)
|
||||||
|
|
@ -25,6 +28,7 @@ type Table struct {
|
||||||
listeners []TableListener
|
listeners []TableListener
|
||||||
inUpdate int32
|
inUpdate int32
|
||||||
refreshRate time.Duration
|
refreshRate time.Duration
|
||||||
|
zeroCount int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTable returns a new table model.
|
// NewTable returns a new table model.
|
||||||
|
|
@ -112,7 +116,6 @@ func (t *Table) refresh(ctx context.Context) {
|
||||||
// AddListener adds a new model listener.
|
// AddListener adds a new model listener.
|
||||||
func (t *Table) AddListener(l TableListener) {
|
func (t *Table) AddListener(l TableListener) {
|
||||||
t.listeners = append(t.listeners, l)
|
t.listeners = append(t.listeners, l)
|
||||||
t.fireTableChanged(*t.data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveListener delete a listener from the list.
|
// RemoveListener delete a listener from the list.
|
||||||
|
|
@ -131,6 +134,12 @@ func (t *Table) RemoveListener(l TableListener) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) fireTableChanged(data render.TableData) {
|
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 {
|
for _, l := range t.listeners {
|
||||||
l.TableDataChanged(data)
|
l.TableDataChanged(data)
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +161,7 @@ func (t *Table) reconcile(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
m, ok := Registry[string(t.gvr)]
|
m, ok := Registry[string(t.gvr)]
|
||||||
if !ok {
|
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{
|
m = ResourceMeta{
|
||||||
Model: &Generic{},
|
Model: &Generic{},
|
||||||
Renderer: &render.Generic{},
|
Renderer: &render.Generic{},
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,12 @@ package render
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
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/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
@ -28,7 +31,7 @@ func (HorizontalPodAutoscaler) Header(ns string) HeaderRow {
|
||||||
return append(h,
|
return append(h,
|
||||||
Header{Name: "NAME"},
|
Header{Name: "NAME"},
|
||||||
Header{Name: "REFERENCE"},
|
Header{Name: "REFERENCE"},
|
||||||
Header{Name: "TARGETS"},
|
Header{Name: "TARGETS%"},
|
||||||
Header{Name: "MINPODS", Align: tview.AlignRight},
|
Header{Name: "MINPODS", Align: tview.AlignRight},
|
||||||
Header{Name: "MAXPODS", Align: tview.AlignRight},
|
Header{Name: "MAXPODS", Align: tview.AlignRight},
|
||||||
Header{Name: "REPLICAS", 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)
|
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
|
var hpa autoscalingv1.HorizontalPodAutoscaler
|
||||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &hpa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -57,7 +75,59 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
|
||||||
r.Fields = append(r.Fields,
|
r.Fields = append(r.Fields,
|
||||||
hpa.ObjectMeta.Name,
|
hpa.ObjectMeta.Name,
|
||||||
hpa.Spec.ScaleTargetRef.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.MinReplicas)),
|
||||||
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
|
strconv.Itoa(int(hpa.Spec.MaxReplicas)),
|
||||||
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
strconv.Itoa(int(hpa.Status.CurrentReplicas)),
|
||||||
|
|
@ -70,7 +140,7 @@ func (h HorizontalPodAutoscaler) Render(o interface{}, ns string, r *Row) error
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
|
func toMetricsV1(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
if status.CurrentCPUUtilizationPercentage != nil {
|
if status.CurrentCPUUtilizationPercentage != nil {
|
||||||
current = strconv.Itoa(int(*status.CurrentCPUUtilizationPercentage)) + "%"
|
current = strconv.Itoa(int(*status.CurrentCPUUtilizationPercentage)) + "%"
|
||||||
|
|
@ -82,3 +152,176 @@ func toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalin
|
||||||
}
|
}
|
||||||
return current + "/" + target + "%"
|
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()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,12 +178,6 @@ func (a *App) redrawCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusReset reset log back to normal.
|
|
||||||
func (a *App) StatusReset() {
|
|
||||||
a.Logo().Reset()
|
|
||||||
a.Draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
// View Accessora...
|
// View Accessora...
|
||||||
|
|
||||||
// Crumbs return app crumba.
|
// Crumbs return app crumba.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultPrompt = "%c> %s"
|
const defaultPrompt = "%c> %s"
|
||||||
|
|
@ -86,7 +85,6 @@ func (c *Command) BufferActive(f bool, k BufferKind) {
|
||||||
c.SetBackgroundColor(c.styles.BgColor())
|
c.SetBackgroundColor(c.styles.BgColor())
|
||||||
c.Clear()
|
c.Clear()
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("Command activated: %t", c.activated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func colorFor(k BufferKind) tcell.Color {
|
func colorFor(k BufferKind) tcell.Color {
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ func styleTitle(rc int, ns, base, path, buff string, styles *config.Styles) stri
|
||||||
rc--
|
rc--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base = strings.Title(base)
|
||||||
if ns == render.AllNamespaces {
|
if ns == render.AllNamespaces {
|
||||||
ns = render.NamespaceAll
|
ns = render.NamespaceAll
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.Flash().Info(msg)
|
||||||
a.setIndicator(l, msg)
|
a.setIndicator(l, msg)
|
||||||
a.setLogo(l, msg)
|
a.setLogo(l, msg)
|
||||||
a.Draw()
|
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) {
|
func (a *App) setLogo(l ui.FlashLevel, msg string) {
|
||||||
switch l {
|
switch l {
|
||||||
case ui.FlashErr:
|
case ui.FlashErr:
|
||||||
|
|
|
||||||
|
|
@ -13,23 +13,17 @@ import (
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const resultTitle = "Benchmark Results"
|
|
||||||
|
|
||||||
// Benchmark represents a service benchmark results view.
|
// Benchmark represents a service benchmark results view.
|
||||||
type Benchmark struct {
|
type Benchmark struct {
|
||||||
ResourceViewer
|
ResourceViewer
|
||||||
|
|
||||||
details *Details
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBench returns a new viewer.
|
// NewBench returns a new viewer.
|
||||||
func NewBenchmark(gvr client.GVR) ResourceViewer {
|
func NewBenchmark(gvr client.GVR) ResourceViewer {
|
||||||
b := Benchmark{
|
b := Benchmark{
|
||||||
ResourceViewer: NewBrowser(gvr),
|
ResourceViewer: NewBrowser(gvr),
|
||||||
details: NewDetails(resultTitle),
|
|
||||||
}
|
}
|
||||||
b.GetTable().SetBorderFocusColor(tcell.ColorSeaGreen)
|
b.GetTable().SetBorderFocusColor(tcell.ColorSeaGreen)
|
||||||
b.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorSeaGreen, tcell.AttrNone)
|
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) {
|
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())
|
data, err := readBenchFile(app.Config, b.benchFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Flash().Errf("Unable to load bench file %s", err)
|
app.Flash().Errf("Unable to load bench file %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b.details.SetText(data)
|
details := NewDetails(b.App(), "Benchmark", fileToSubject(path)).Update(data)
|
||||||
b.details.SetSubject(fileToSubject(path))
|
if err := app.inject(details); err != nil {
|
||||||
if err := app.inject(b.details); err != nil {
|
|
||||||
app.Flash().Err(err)
|
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 {
|
func (b *Benchmark) benchFile() string {
|
||||||
r := b.GetTable().GetSelectedRowIndex()
|
r := b.GetTable().GetSelectedRowIndex()
|
||||||
return ui.TrimCell(b.GetTable().SelectTable, r, 7)
|
return ui.TrimCell(b.GetTable().SelectTable, r, 7)
|
||||||
|
|
@ -75,6 +60,12 @@ func (b *Benchmark) benchFile() string {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// 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 {
|
func benchDir(cfg *config.Config) string {
|
||||||
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster)
|
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ type Browser struct {
|
||||||
|
|
||||||
namespaces map[int]string
|
namespaces map[int]string
|
||||||
gvr client.GVR
|
gvr client.GVR
|
||||||
envFn EnvFunc
|
|
||||||
meta metav1.APIResource
|
meta metav1.APIResource
|
||||||
accessor dao.Accessor
|
accessor dao.Accessor
|
||||||
|
envFn EnvFunc
|
||||||
contextFn ContextFunc
|
contextFn ContextFunc
|
||||||
bindKeysFn BindKeysFunc
|
bindKeysFn BindKeysFunc
|
||||||
cancelFn context.CancelFunc
|
cancelFn context.CancelFunc
|
||||||
|
|
@ -72,7 +72,6 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
b.bindKeysFn(b.Actions())
|
b.bindKeysFn(b.Actions())
|
||||||
}
|
}
|
||||||
b.BaseTitle = b.meta.Kind
|
b.BaseTitle = b.meta.Kind
|
||||||
b.SetTitle(" [orange:i:]LOADING... ")
|
|
||||||
b.accessor, err = dao.AccessorFor(b.app.factory, b.gvr)
|
b.accessor, err = dao.AccessorFor(b.app.factory, b.gvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -85,6 +84,7 @@ func (b *Browser) Init(ctx context.Context) error {
|
||||||
b.Select(1, 0)
|
b.Select(1, 0)
|
||||||
}
|
}
|
||||||
b.GetModel().AddListener(b)
|
b.GetModel().AddListener(b)
|
||||||
|
b.App().Status(ui.FlashWarn, "Loading...")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -181,6 +181,23 @@ func (b *Browser) GVR() string { return string(b.gvr) }
|
||||||
// GetTable returns the underlying table.
|
// GetTable returns the underlying table.
|
||||||
func (b *Browser) GetTable() *Table { return b.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()...
|
// Actions()...
|
||||||
|
|
||||||
|
|
@ -218,6 +235,7 @@ func (b *Browser) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
func (b *Browser) refreshCmd(*tcell.EventKey) *tcell.EventKey {
|
func (b *Browser) refreshCmd(*tcell.EventKey) *tcell.EventKey {
|
||||||
b.app.Flash().Info("Refreshing...")
|
b.app.Flash().Info("Refreshing...")
|
||||||
b.refresh()
|
b.refresh()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,7 +244,6 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if len(selections) == 0 {
|
if len(selections) == 0 {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("DEL SELECTIONS %#v", selections)
|
|
||||||
|
|
||||||
b.Stop()
|
b.Stop()
|
||||||
defer b.Start()
|
defer b.Start()
|
||||||
|
|
@ -235,52 +252,57 @@ func (b *Browser) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if len(selections) > 1 {
|
if len(selections) > 1 {
|
||||||
msg = fmt.Sprintf("Delete %d marked %s?", len(selections), b.gvr)
|
msg = fmt.Sprintf("Delete %d marked %s?", len(selections), b.gvr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelFn := func() {}
|
|
||||||
if dao.IsK9sMeta(b.meta) {
|
if dao.IsK9sMeta(b.meta) {
|
||||||
dialog.ShowConfirm(b.app.Content.Pages, "Confirm Delete", msg, func() {
|
b.simpleDelete(selections, msg)
|
||||||
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)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
b.resourceDelete(selections, msg)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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 {
|
func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := b.GetSelectedItem()
|
path := b.GetSelectedItem()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
@ -299,18 +321,7 @@ func (b *Browser) describeResource(app *App, _, _, sel string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
details := NewDetails("Describe")
|
details := NewDetails(b.App(), "Describe", sel).Update(yaml)
|
||||||
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()
|
|
||||||
if err := b.app.inject(details); err != nil {
|
if err := b.app.inject(details); err != nil {
|
||||||
b.app.Flash().Err(err)
|
b.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
@ -322,7 +333,6 @@ func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
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())
|
o, err := b.app.factory.Get(string(b.gvr), path, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.app.Flash().Errf("Unable to get resource %q -- %s", b.gvr, err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
details := NewDetails("YAML")
|
details := NewDetails(b.app, "YAML", path).Update(raw)
|
||||||
details.SetSubject(path)
|
|
||||||
details.SetTextColor(b.app.Styles.FgColor())
|
|
||||||
details.SetText(colorizeYAML(b.app.Styles.Views().Yaml, raw))
|
|
||||||
details.ScrollToBeginning()
|
|
||||||
if err := b.app.inject(details); err != nil {
|
if err := b.app.inject(details); err != nil {
|
||||||
b.App().Flash().Err(err)
|
b.App().Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +405,6 @@ func (b *Browser) setNamespace(ns string) {
|
||||||
if ns == render.NamespaceAll {
|
if ns == render.NamespaceAll {
|
||||||
ns = render.AllNamespaces
|
ns = render.AllNamespaces
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("!!!!!! SETTING NS %q", ns)
|
|
||||||
b.GetModel().SetNamespace(ns)
|
b.GetModel().SetNamespace(ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,21 +432,6 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
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 {
|
func (b *Browser) defaultContext() context.Context {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,20 @@ type Details struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDetails returns a details viewer.
|
// NewDetails returns a details viewer.
|
||||||
func NewDetails(title string) *Details {
|
func NewDetails(app *App, title, subject string) *Details {
|
||||||
return &Details{
|
d := Details{
|
||||||
TextView: tview.NewTextView(),
|
TextView: tview.NewTextView(),
|
||||||
|
app: app,
|
||||||
title: title,
|
title: title,
|
||||||
|
subject: subject,
|
||||||
actions: make(ui.KeyActions),
|
actions: make(ui.KeyActions),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (d *Details) Init(ctx context.Context) error {
|
func (d *Details) Init(_ context.Context) error {
|
||||||
var err error
|
|
||||||
if d.app, err = extractApp(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.title != "" {
|
if d.title != "" {
|
||||||
d.SetBorder(true)
|
d.SetBorder(true)
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +67,12 @@ func (d *Details) StylesChanged(s *config.Styles) {
|
||||||
d.Update(d.buff)
|
d.Update(d.buff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Details) Update(buff string) {
|
func (d *Details) Update(buff string) *Details {
|
||||||
d.buff = buff
|
d.buff = buff
|
||||||
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, buff))
|
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, buff))
|
||||||
d.ScrollToBeginning()
|
d.ScrollToBeginning()
|
||||||
|
|
||||||
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Details) Actions() ui.KeyActions {
|
func (d *Details) Actions() ui.KeyActions {
|
||||||
|
|
@ -107,10 +108,10 @@ func (d *Details) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if key == tcell.KeyRune {
|
if key == tcell.KeyRune {
|
||||||
key = tcell.Key(evt.Rune())
|
key = tcell.Key(evt.Rune())
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, ok := d.actions[key]; ok {
|
if a, ok := d.actions[key]; ok {
|
||||||
return a.Action(evt)
|
return a.Action(evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,16 +71,15 @@ func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
l.indicator = NewLogIndicator(l.app.Styles)
|
l.indicator = NewLogIndicator(l.app.Styles)
|
||||||
l.AddItem(l.indicator, 1, 1, false)
|
l.AddItem(l.indicator, 1, 1, false)
|
||||||
|
|
||||||
l.logs = NewDetails("")
|
l.logs = NewDetails(l.app, "", "")
|
||||||
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)
|
|
||||||
if err = l.logs.Init(ctx); err != nil {
|
if err = l.logs.Init(ctx); err != nil {
|
||||||
return err
|
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.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.AddItem(l.logs, 0, 1, true)
|
||||||
l.bindKeys()
|
l.bindKeys()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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()
|
sel := n.GetTable().GetSelectedItem()
|
||||||
log.Debug().Msgf("------ VIEW NODE %q", sel)
|
gvr := client.GVR(n.GVR()).AsGVR()
|
||||||
o, err := n.App().factory.Client().DynDialOrDie().Resource(client.GVR(n.GVR()).AsGVR()).Get(sel, metav1.GetOptions{})
|
o, err := n.App().factory.Client().DynDialOrDie().Resource(gvr).Get(sel, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.App().Flash().Errf("Unable to get resource %q -- %s", n.GVR(), err)
|
n.App().Flash().Errf("Unable to get resource %q -- %s", n.GVR(), err)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -59,11 +58,7 @@ func (n *Node) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
details := NewDetails("YAML")
|
details := NewDetails(n.App(), "YAML", sel).Update(raw)
|
||||||
details.SetSubject(sel)
|
|
||||||
details.SetTextColor(n.App().Styles.FgColor())
|
|
||||||
details.SetText(colorizeYAML(n.App().Styles.Views().Yaml, raw))
|
|
||||||
details.ScrollToBeginning()
|
|
||||||
if err := n.App().inject(details); err != nil {
|
if err := n.App().inject(details); err != nil {
|
||||||
n.App().Flash().Err(err)
|
n.App().Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,10 @@ func (p *PortForward) showBenchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
func (p *PortForward) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (p *PortForward) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if p.bench != nil {
|
if p.bench != nil {
|
||||||
log.Debug().Msg(">>> Benchmark cancelFned!!")
|
log.Debug().Msg(">>> Benchmark cancelFned!!")
|
||||||
p.App().status(ui.FlashErr, "Benchmark Camceled!")
|
p.App().Status(ui.FlashErr, "Benchmark Camceled!")
|
||||||
p.bench.Cancel()
|
p.bench.Cancel()
|
||||||
}
|
}
|
||||||
p.App().StatusReset()
|
p.App().ClearStatus()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -97,11 +97,11 @@ func (p *PortForward) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
var err error
|
var err error
|
||||||
if p.bench, err = perf.NewBenchmark(base, cfg); err != nil {
|
if p.bench, err = perf.NewBenchmark(base, cfg); err != nil {
|
||||||
p.App().Flash().Errf("Bench failed %v", err)
|
p.App().Flash().Errf("Bench failed %v", err)
|
||||||
p.App().StatusReset()
|
p.App().ClearStatus()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.App().status(ui.FlashWarn, "Benchmark in progress...")
|
p.App().Status(ui.FlashWarn, "Benchmark in progress...")
|
||||||
log.Debug().Msg("Bench starting...")
|
log.Debug().Msg("Bench starting...")
|
||||||
go p.runBenchmark()
|
go p.runBenchmark()
|
||||||
|
|
||||||
|
|
@ -113,15 +113,15 @@ func (p *PortForward) runBenchmark() {
|
||||||
log.Debug().Msg("Bench Completed!")
|
log.Debug().Msg("Bench Completed!")
|
||||||
p.App().QueueUpdate(func() {
|
p.App().QueueUpdate(func() {
|
||||||
if p.bench.Canceled() {
|
if p.bench.Canceled() {
|
||||||
p.App().status(ui.FlashInfo, "Benchmark cancelFned")
|
p.App().Status(ui.FlashInfo, "Benchmark canceled")
|
||||||
} else {
|
} else {
|
||||||
p.App().status(ui.FlashInfo, "Benchmark Completed!")
|
p.App().Status(ui.FlashInfo, "Benchmark Completed!")
|
||||||
p.bench.Cancel()
|
p.bench.Cancel()
|
||||||
}
|
}
|
||||||
p.bench = nil
|
p.bench = nil
|
||||||
go func() {
|
go func() {
|
||||||
<-time.After(2 * time.Second)
|
<-time.After(2 * time.Second)
|
||||||
p.App().QueueUpdate(func() { p.App().StatusReset() })
|
p.App().QueueUpdate(func() { p.App().ClearStatus() })
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,7 @@ func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
details := NewDetails("Decoder")
|
details := NewDetails(s.App(), "Secret Decoder", path).Update(string(raw))
|
||||||
details.SetSubject(path)
|
|
||||||
details.SetTextColor(s.App().Styles.FgColor())
|
|
||||||
details.SetText(colorizeYAML(s.App().Styles.Views().Yaml, string(raw)))
|
|
||||||
details.ScrollToBeginning()
|
|
||||||
if err := s.App().inject(details); err != nil {
|
if err := s.App().inject(details); err != nil {
|
||||||
s.App().Flash().Err(err)
|
s.App().Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,10 @@ func (s *Service) showPods(app *App, ns, gvr, path string) {
|
||||||
func (s *Service) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (s *Service) benchStopCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if s.bench != nil {
|
if s.bench != nil {
|
||||||
log.Debug().Msg(">>> Benchmark canceled!!")
|
log.Debug().Msg(">>> Benchmark canceled!!")
|
||||||
s.App().status(ui.FlashErr, "Benchmark Canceled!")
|
s.App().Status(ui.FlashErr, "Benchmark Canceled!")
|
||||||
s.bench.Cancel()
|
s.bench.Cancel()
|
||||||
}
|
}
|
||||||
s.App().StatusReset()
|
s.App().ClearStatus()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -136,13 +136,14 @@ func (s *Service) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
if err := s.runBenchmark(port, cfg); err != nil {
|
if err := s.runBenchmark(port, cfg); err != nil {
|
||||||
s.App().Flash().Errf("Benchmark failed %v", err)
|
s.App().Flash().Errf("Benchmark failed %v", err)
|
||||||
s.App().StatusReset()
|
s.App().ClearStatus()
|
||||||
s.bench = nil
|
s.bench = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BOZO!! Refactor used by forwards
|
||||||
func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
|
func (s *Service) runBenchmark(port string, cfg config.BenchConfig) error {
|
||||||
if cfg.HTTP.Host == "" {
|
if cfg.HTTP.Host == "" {
|
||||||
return fmt.Errorf("Invalid benchmark host %q", 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.App().status(ui.FlashWarn, "Benchmark in progress...")
|
s.App().Status(ui.FlashWarn, "Benchmark in progress...")
|
||||||
log.Debug().Msg("Bench starting...")
|
log.Debug().Msg("Bench starting...")
|
||||||
go s.bench.Run(s.App().Config.K9s.CurrentCluster, s.benchDone)
|
go s.bench.Run(s.App().Config.K9s.CurrentCluster, s.benchDone)
|
||||||
|
|
||||||
|
|
@ -165,9 +166,9 @@ func (s *Service) benchDone() {
|
||||||
log.Debug().Msg("Bench Completed!")
|
log.Debug().Msg("Bench Completed!")
|
||||||
s.App().QueueUpdate(func() {
|
s.App().QueueUpdate(func() {
|
||||||
if s.bench.Canceled() {
|
if s.bench.Canceled() {
|
||||||
s.App().status(ui.FlashInfo, "Benchmark canceled")
|
s.App().Status(ui.FlashInfo, "Benchmark canceled")
|
||||||
} else {
|
} else {
|
||||||
s.App().status(ui.FlashInfo, "Benchmark Completed!")
|
s.App().Status(ui.FlashInfo, "Benchmark Completed!")
|
||||||
s.bench.Cancel()
|
s.bench.Cancel()
|
||||||
}
|
}
|
||||||
s.bench = nil
|
s.bench = nil
|
||||||
|
|
@ -178,6 +179,6 @@ func (s *Service) benchDone() {
|
||||||
func benchTimedOut(app *App) {
|
func benchTimedOut(app *App) {
|
||||||
<-time.After(2 * time.Second)
|
<-time.After(2 * time.Second)
|
||||||
app.QueueUpdate(func() {
|
app.QueueUpdate(func() {
|
||||||
app.StatusReset()
|
app.ClearStatus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ func (t *Table) Init(ctx context.Context) (err error) {
|
||||||
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
|
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
|
||||||
t.Table.Init(ctx)
|
t.Table.Init(ctx)
|
||||||
t.bindKeys()
|
t.bindKeys()
|
||||||
|
|
||||||
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
t.GetModel().SetRefreshRate(time.Duration(t.app.Config.K9s.GetRefreshRate()) * time.Second)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// WaitForCachesync waits for all factories to update their cache.
|
||||||
func (f *Factory) WaitForCacheSync() {
|
func (f *Factory) WaitForCacheSync() {
|
||||||
for _, fac := range f.factories {
|
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.
|
// RegisterForwarder registers a new portforward for a given container.
|
||||||
func (f *Factory) AddForwarder(pf Forwarder) {
|
func (f *Factory) AddForwarder(pf Forwarder) {
|
||||||
f.forwarders[pf.Path()] = pf
|
f.forwarders[pf.Path()] = pf
|
||||||
f.forwarders.Dump()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteForwarder deletes portforward for a given container.
|
// DeleteForwarder deletes portforward for a given container.
|
||||||
func (f *Factory) DeleteForwarder(path string) {
|
func (f *Factory) DeleteForwarder(path string) {
|
||||||
f.forwarders.Dump()
|
|
||||||
fwd, ok := f.forwarders[path]
|
fwd, ok := f.forwarders[path]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn().Msgf("Unable to delete portForward %q", path)
|
log.Warn().Msgf("Unable to delete portForward %q", path)
|
||||||
|
|
@ -111,7 +112,6 @@ func (f *Factory) DeleteForwarder(path string) {
|
||||||
}
|
}
|
||||||
fwd.Stop()
|
fwd.Stop()
|
||||||
delete(f.forwarders, path)
|
delete(f.forwarders, path)
|
||||||
f.forwarders.Dump()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forwards returns all portforwards.
|
// Forwards returns all portforwards.
|
||||||
|
|
@ -146,10 +146,11 @@ func (f *Factory) isClusterWide() bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Factory) preload(ns string) {
|
func (f *Factory) preload(_ string) {
|
||||||
// verbs := []string{"get", "list", "watch"}
|
// BOZO!!
|
||||||
|
verbs := []string{"get", "list", "watch"}
|
||||||
// _, _ = f.CanForResource(ns, "v1/pods", verbs...)
|
// _, _ = 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(clusterScope, "rbac.authorization.k8s.io/v1/clusterroles", verbs...)
|
||||||
// _, _ = f.CanForResource(allNamespaces, "rbac.authorization.k8s.io/v1/roles", verbs...)
|
// _, _ = f.CanForResource(allNamespaces, "rbac.authorization.k8s.io/v1/roles", verbs...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ func NewForwarders() Forwarders {
|
||||||
|
|
||||||
// KillAll stops and delete all port-forwards.
|
// KillAll stops and delete all port-forwards.
|
||||||
func (ff Forwarders) DeleteAll() {
|
func (ff Forwarders) DeleteAll() {
|
||||||
ff.Dump()
|
|
||||||
for k, f := range ff {
|
for k, f := range ff {
|
||||||
log.Debug().Msgf("Deleting forwarder %s", f.Path())
|
log.Debug().Msgf("Deleting forwarder %s", f.Path())
|
||||||
f.Stop()
|
f.Stop()
|
||||||
|
|
@ -51,9 +50,6 @@ func (ff Forwarders) DeleteAll() {
|
||||||
|
|
||||||
// Kill stops and delete a port-forwards associated with pod.
|
// Kill stops and delete a port-forwards associated with pod.
|
||||||
func (ff Forwarders) Kill(path string) int {
|
func (ff Forwarders) Kill(path string) int {
|
||||||
ff.Dump()
|
|
||||||
|
|
||||||
log.Debug().Msgf("Delete port-forward %q", path)
|
|
||||||
hasContainer := strings.Contains(path, ":")
|
hasContainer := strings.Contains(path, ":")
|
||||||
var stats int
|
var stats int
|
||||||
for k, f := range ff {
|
for k, f := range ff {
|
||||||
|
|
@ -63,7 +59,7 @@ func (ff Forwarders) Kill(path string) int {
|
||||||
}
|
}
|
||||||
if victim == path {
|
if victim == path {
|
||||||
stats++
|
stats++
|
||||||
log.Debug().Msgf("Stopping and delete port-forward %s", k)
|
log.Debug().Msgf("Stop + Delete port-forward %s", k)
|
||||||
f.Stop()
|
f.Stop()
|
||||||
delete(ff, k)
|
delete(ff, k)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue