axe node level resources summary + fix auto-suggest

mine
derailed 2020-11-05 13:04:16 -07:00
parent c996ab314b
commit 0391c5bd79
16 changed files with 147 additions and 124 deletions

22
go.sum
View File

@ -21,24 +21,37 @@ github.com/Azure/go-autorest v10.8.1+incompatible h1:u0jVQf+a6k6x8A+sT60l6EY9XZu
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.0.0+incompatible h1:r/ug62X9o8vikt53/nkAPmFmzfSrCCAplPH7wa+mK0U=
github.com/Azure/go-autorest v14.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY=
github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.11.10 h1:j5sGbX7uj1ieYYkQ3Mpvewd4DCsEQ+ZeJpqnSM9pjnM=
github.com/Azure/go-autorest/autorest v0.11.10/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@ -89,6 +102,8 @@ github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.29.32 h1:o4I8Qc+h9ht8NXvTHeXZH3EmtSUZ/PC0bg9Wawr+aTA=
github.com/aws/aws-sdk-go v1.29.32/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
github.com/aws/aws-sdk-go v1.35.21 h1:6cMeHzcca+0uweOpUonDYv4DsPp9Qa9PTMYxH+VqDkY=
github.com/aws/aws-sdk-go v1.35.21/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -223,6 +238,8 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
@ -420,6 +437,9 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
@ -745,6 +765,8 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEB
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

View File

@ -26,7 +26,6 @@ import (
const (
cacheSize = 100
cacheExpiry = 5 * time.Minute
cacheMXKey = "metrics"
cacheMXAPIKey = "metricsAPI"
)
@ -46,7 +45,7 @@ type APIClient struct {
}
// NewTestClient for testing ONLY!!
func NewTestClient() *APIClient {
func NewTestAPIClient() *APIClient {
return &APIClient{
config: NewConfig(nil),
cache: cache.NewLRUExpireCache(cacheSize),
@ -61,12 +60,12 @@ func InitConnection(config *Config) (*APIClient, error) {
cache: cache.NewLRUExpireCache(cacheSize),
}
a.connOK = true
_, err := a.supportsMetricsResources()
if err != nil {
if err := a.supportsMetricsResources(); err != nil {
a.connOK = false
return nil, err
}
return &a, err
return &a, nil
}
// ConnectionOK returns connection status.
@ -272,35 +271,12 @@ func (a *APIClient) Config() *Config {
return a.config
}
// HasMetrics returns true if the cluster supports metrics.
// HasMetrics checks if the cluster supports metrics.
func (a *APIClient) HasMetrics() bool {
ok, err := a.supportsMetricsResources()
if !ok || err != nil {
if err := a.supportsMetricsResources(); err != nil {
return false
}
if v, ok := a.cache.Get(cacheMXKey); ok {
flag, k := v.(bool)
return k && flag
}
var metricsOK bool
defer func() {
a.cache.Add(cacheMXKey, metricsOK, cacheExpiry)
}()
dial, err := a.MXDial()
if err != nil {
return metricsOK
}
ctx, cancel := context.WithTimeout(context.Background(), a.config.CallTimeout())
defer cancel()
if _, err := dial.MetricsV1beta1().NodeMetricses().List(ctx, metav1.ListOptions{Limit: 1}); err == nil {
metricsOK = true
} else {
log.Error().Err(err).Msgf("List metrics failed")
}
return metricsOK
return true
}
// Dial returns a handle to api server or die.
@ -425,27 +401,37 @@ func (a *APIClient) reset() {
a.connOK = true
}
func (a *APIClient) supportsMetricsResources() (supported bool, err error) {
if v, ok := a.cache.Get(cacheMXAPIKey); ok {
flag, k := v.(bool)
supported = k && flag
func (a *APIClient) checkCacheBool(key string) (state bool, ok bool) {
v, found := a.cache.Get(key)
if !found {
return
}
if a.config == nil || a.config.flags == nil {
return
state, ok = v.(bool)
return
}
func (a *APIClient) supportsMetricsResources() error {
supported, ok := a.checkCacheBool(cacheMXAPIKey)
if ok {
if supported {
return nil
}
return errors.New("No metrics-server detected")
}
defer func() {
a.cache.Add(cacheMXAPIKey, supported, cacheExpiry)
}()
dial, err := a.CachedDiscovery()
if err != nil {
return false, err
log.Warn().Err(err).Msgf("Unable to dial discovery API")
return err
}
apiGroups, err := dial.ServerGroups()
if err != nil {
log.Debug().Msgf("Unable to access servergroups %#v", err)
return
log.Warn().Err(err).Msgf("Unable to retrieve server groups")
return err
}
for _, grp := range apiGroups.Groups {
if grp.Name != metricsapi.GroupName {
@ -453,11 +439,11 @@ func (a *APIClient) supportsMetricsResources() (supported bool, err error) {
}
if checkMetricsVersion(grp) {
supported = true
return
return nil
}
}
return
return errors.New("No metrics-server detected")
}
func checkMetricsVersion(grp metav1.APIGroup) bool {

View File

@ -0,0 +1,54 @@
package client
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCheckCacheBool(t *testing.T) {
c := NewTestAPIClient()
const key = "fred"
uu := map[string]struct {
key string
val interface{}
found, actual, sleep bool
}{
"setTrue": {
key: key,
val: true,
found: true,
actual: true,
},
"setFalse": {
key: key,
val: false,
found: true,
},
"missing": {
key: "blah",
val: false,
},
"expired": {
key: key,
val: true,
sleep: true,
},
}
expiry := 1 * time.Millisecond
for k := range uu {
u := uu[k]
c.cache.Add(key, u.val, expiry)
if u.sleep {
time.Sleep(expiry)
}
t.Run(k, func(t *testing.T) {
val, ok := c.checkCacheBool(u.key)
assert.Equal(t, u.found, ok)
assert.Equal(t, u.actual, val)
})
}
}

View File

@ -2,6 +2,7 @@ package client
import (
"context"
"errors"
"fmt"
"math"
"strconv"
@ -90,7 +91,7 @@ func (m *MetricsServer) ClusterLoad(nos *v1.NodeList, nmx *mv1beta1.NodeMetricsL
func (m *MetricsServer) checkAccess(ns, gvr, msg string) error {
if !m.HasMetrics() {
return fmt.Errorf("No metrics-server detected on cluster")
return errors.New("No metrics-server detected on cluster")
}
auth, err := m.CanI(ns, gvr, ListAccess)

View File

@ -115,9 +115,7 @@ func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
err error
)
if withMx, ok := ctx.Value(internal.KeyWithMetrics).(bool); withMx || !ok {
if nmx, err = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx); err != nil {
log.Debug().Err(err).Msgf("No node metrics")
}
nmx, _ = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx)
}
no, err := FetchNode(ctx, n.Factory, path)
@ -142,9 +140,7 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
err error
)
if withMx, ok := ctx.Value(internal.KeyWithMetrics).(bool); withMx || !ok {
if nmx, err = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx); err != nil {
log.Warn().Err(err).Msgf("No node metrics")
}
nmx, _ = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx)
}
labels, _ := ctx.Value(internal.KeyLabels).(string)

View File

@ -65,9 +65,7 @@ func (p *Pod) Get(ctx context.Context, path string) (runtime.Object, error) {
var pmx *mv1beta1.PodMetrics
if withMx, ok := ctx.Value(internal.KeyWithMetrics).(bool); withMx || !ok {
if pmx, err = client.DialMetrics(p.Client()).FetchPodMetrics(ctx, path); err != nil {
log.Debug().Err(err).Msgf("No pod metrics")
}
pmx, _ = client.DialMetrics(p.Client()).FetchPodMetrics(ctx, path)
}
return &render.PodWithMetrics{Raw: u, MX: pmx}, nil

View File

@ -77,7 +77,7 @@ func (p *Popeye) List(ctx context.Context, ns string) ([]runtime.Object, error)
if err != nil {
return nil, err
}
popeye.SetFactory(newPopFactory(p.Factory))
popeye.SetFactory(newPopeyeFactory(p.Factory))
if err = popeye.Init(); err != nil {
return nil, err
}
@ -120,19 +120,19 @@ type popFactory struct {
var _ types.Factory = (*popFactory)(nil)
func newPopFactory(f Factory) *popFactory {
func newPopeyeFactory(f Factory) *popFactory {
return &popFactory{Factory: f}
}
func (p *popFactory) Client() types.Connection {
return &popConnection{Connection: p.Factory.Client()}
return &popeyeConnection{Connection: p.Factory.Client()}
}
type popConnection struct {
type popeyeConnection struct {
client.Connection
}
var _ types.Connection = (*popConnection)(nil)
var _ types.Connection = (*popeyeConnection)(nil)
func (c *popConnection) Config() types.Config {
func (c *popeyeConnection) Config() types.Config {
return c.Connection.Config()
}

View File

@ -105,11 +105,6 @@ func (f *FishBuff) Notify(delete bool) {
if f.suggestionFn == nil {
return
}
ss := f.suggestionFn(string(f.buff))
if len(ss) == 1 && !delete {
f.SetText(string(string(f.buff) + ss[0]))
return
}
f.fireSuggestionChanged(f.suggestionFn(string(f.buff)))
}

View File

@ -8,23 +8,6 @@ import (
"github.com/stretchr/testify/assert"
)
func TestFishExact(t *testing.T) {
m := mockSuggestionListener{}
f := model.NewFishBuff(' ', model.FilterBuffer)
f.AddListener(&m)
f.SetSuggestionFn(func(text string) sort.StringSlice {
return sort.StringSlice{"lee"}
})
f.Add('b')
f.SetActive(true)
assert.True(t, m.active)
assert.Equal(t, 1, m.buff)
assert.Equal(t, 0, m.sugg)
assert.Equal(t, "blee", m.text)
}
func TestFishAdd(t *testing.T) {
m := mockSuggestionListener{}
@ -37,8 +20,8 @@ func TestFishAdd(t *testing.T) {
f.SetActive(true)
assert.True(t, m.active)
assert.Equal(t, 1, m.buff)
assert.Equal(t, 1, m.sugg)
assert.Equal(t, 1, m.changeCount)
assert.Equal(t, 1, m.suggCount)
assert.Equal(t, "blee", m.suggestion)
c, ok := f.CurrentSuggestion()
@ -66,8 +49,8 @@ func TestFishDelete(t *testing.T) {
f.Delete()
f.SetActive(true)
assert.Equal(t, 2, m.buff)
assert.Equal(t, 2, m.sugg)
assert.Equal(t, 2, m.changeCount)
assert.Equal(t, 2, m.suggCount)
assert.True(t, m.active)
assert.Equal(t, "blee", m.suggestion)
@ -87,13 +70,13 @@ func TestFishDelete(t *testing.T) {
// Helpers...
type mockSuggestionListener struct {
buff, sugg int
suggestion, text string
active bool
changeCount, suggCount int
suggestion, text string
active bool
}
func (m *mockSuggestionListener) BufferChanged(s string) {
m.buff++
m.changeCount++
}
func (m *mockSuggestionListener) BufferCompleted(s string) {
@ -106,5 +89,5 @@ func (m *mockSuggestionListener) BufferActive(state bool, kind model.BufferKind)
func (m *mockSuggestionListener) SuggestionChanged(text, sugg string) {
m.suggestion = sugg
m.sugg++
m.suggCount++
}

View File

@ -179,7 +179,7 @@ type testFactory struct {
var _ dao.Factory = testFactory{}
func (f testFactory) Client() client.Connection {
return client.NewTestClient()
return client.NewTestAPIClient()
}
func (f testFactory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
if len(f.rows) > 0 {

View File

@ -86,7 +86,7 @@ type tableFactory struct {
var _ dao.Factory = tableFactory{}
func (f tableFactory) Client() client.Connection {
return client.NewTestClient()
return client.NewTestAPIClient()
}
func (f tableFactory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
if len(f.rows) > 0 {

View File

@ -219,8 +219,8 @@ func ToContainerState(s v1.ContainerState) string {
}
const (
on string = "on"
off = "off"
on = "on"
off = "off"
)
func probe(p *v1.Probe) string {

View File

@ -248,14 +248,6 @@ func mapToIfc(m interface{}) (s string) {
return
}
func toMcPerc(v1, v2 int64) string {
return toMc(v1) + " (" + strconv.Itoa(client.ToPercentage(v1, v2)) + "%)"
}
func toMiPerc(v1, v2 int64) string {
return toMi(v1) + " (" + strconv.Itoa(client.ToPercentage(v1, v2)) + "%)"
}
func toMc(v int64) string {
if v == 0 {
return ZeroValue

View File

@ -10,7 +10,6 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/tview"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -45,10 +44,6 @@ func (Node) Header(_ string) Header {
HeaderColumn{Name: "MEM", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "%CPU", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "%MEM", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU/R", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU/L", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "MEM/R", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "MEM/L", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU/A", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "MEM/A", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "LABELS", Wide: true},
@ -78,16 +73,6 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
iIP, eIP = missing(iIP), missing(eIP)
c, p, a := gatherNodeMX(&no, oo.MX)
trc, trm, tlc, tlm := new(resource.Quantity), new(resource.Quantity), new(resource.Quantity), new(resource.Quantity)
for _, p := range oo.Pods {
rcpu, rmem := podRequests(p.Spec)
trc.Add(rcpu)
trm.Add(rmem)
lcpu, lmem := podLimits(p.Spec)
tlc.Add(lcpu)
tlm.Add(lmem)
}
statuses := make(sort.StringSlice, 10)
status(no.Status.Conditions, no.Spec.Unschedulable, statuses)
sort.Sort(statuses)
@ -109,10 +94,6 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
toMi(c.mem),
strconv.Itoa(p.rCPU()),
strconv.Itoa(p.rMEM()),
toMcPerc(trc.MilliValue(), a.cpu),
toMcPerc(tlc.MilliValue(), a.cpu),
toMiPerc(trm.Value(), a.mem),
toMiPerc(tlm.Value(), a.mem),
toMc(a.cpu),
toMi(a.mem),
mapToStr(no.Labels),

View File

@ -21,8 +21,8 @@ func TestNodeRender(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "minikube", r.ID)
e := render.Fields{"minikube", "Ready", "master", "v1.15.2", "4.15.0", "192.168.64.107", "<none>", "0", "10", "20", "0", "0", "0 (0%)", "0 (0%)", "0 (0%)", "0 (0%)", "4000", "7874"}
assert.Equal(t, e, r.Fields[:18])
e := render.Fields{"minikube", "Ready", "master", "v1.15.2", "4.15.0", "192.168.64.107", "<none>", "0", "10", "20", "0", "0", "4000", "7874"}
assert.Equal(t, e, r.Fields[:14])
}
func BenchmarkNodeRender(b *testing.B) {

View File

@ -4,12 +4,14 @@ import (
"fmt"
"os"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
var _ model.ClusterInfoListener = (*ClusterInfo)(nil)
@ -46,9 +48,22 @@ func (c *ClusterInfo) StylesChanged(s *config.Styles) {
c.updateStyle()
}
func (c *ClusterInfo) hasMetrics() bool {
mx := c.app.Conn().HasMetrics()
if mx {
auth, err := c.app.Conn().CanI("", "metrics.k8s.io/v1beta1/nodes", client.ListAccess)
if err != nil {
log.Warn().Err(err).Msgf("No nodes metrics access")
}
mx = auth
}
return mx
}
func (c *ClusterInfo) layout() {
for row, section := range []string{"Context", "Cluster", "User", "K9s Rev", "K8s Rev", "CPU", "MEM"} {
if (section == "CPU" || section == "MEM") && !c.app.Conn().HasMetrics() {
if (section == "CPU" || section == "MEM") && !c.hasMetrics() {
continue
}
c.SetCell(row, 0, c.sectionCell(section))
@ -96,7 +111,7 @@ func (c *ClusterInfo) ClusterInfoChanged(prev, curr model.ClusterMeta) {
row = c.setCell(row, curr.User)
row = c.setCell(row, fmt.Sprintf("%s [%d]", curr.K9sVer, os.Getpid()))
row = c.setCell(row, curr.K8sVer)
if c.app.Conn().HasMetrics() {
if c.hasMetrics() {
row = c.setCell(row, ui.AsPercDelta(prev.Cpu, curr.Cpu))
_ = c.setCell(row, ui.AsPercDelta(prev.Mem, curr.Mem))
c.setDefCon(curr.Cpu, curr.Mem)