axe node level resources summary + fix auto-suggest
parent
c996ab314b
commit
0391c5bd79
22
go.sum
22
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue