cleanup and bug fixes
parent
4045cb56a4
commit
372b4d8e09
1
go.mod
1
go.mod
|
|
@ -4,6 +4,7 @@ go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.2
|
github.com/atotto/clipboard v0.1.2
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0
|
github.com/cenkalti/backoff/v4 v4.1.0
|
||||||
github.com/derailed/popeye v0.8.10
|
github.com/derailed/popeye v0.8.10
|
||||||
github.com/derailed/tview v0.4.6
|
github.com/derailed/tview v0.4.6
|
||||||
|
|
|
||||||
1
go.sum
1
go.sum
|
|
@ -111,6 +111,7 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe
|
||||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||||
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
|
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
|
|
||||||
|
|
@ -264,29 +264,29 @@ func (a *APIClient) HasMetrics() bool {
|
||||||
if !ok || err != nil {
|
if !ok || err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
v, ok := a.cache.Get(cacheMXKey)
|
if v, ok := a.cache.Get(cacheMXKey); ok {
|
||||||
if ok {
|
|
||||||
flag, k := v.(bool)
|
flag, k := v.(bool)
|
||||||
return k && flag
|
return k && flag
|
||||||
}
|
}
|
||||||
|
|
||||||
var flag bool
|
var metricsOK bool
|
||||||
|
defer func() {
|
||||||
|
a.cache.Add(cacheMXKey, metricsOK, cacheExpiry)
|
||||||
|
}()
|
||||||
dial, err := a.MXDial()
|
dial, err := a.MXDial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.cache.Add(cacheMXKey, flag, cacheExpiry)
|
return metricsOK
|
||||||
return flag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), a.config.CallTimeout())
|
ctx, cancel := context.WithTimeout(context.Background(), a.config.CallTimeout())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if _, err := dial.MetricsV1beta1().NodeMetricses().List(ctx, metav1.ListOptions{Limit: 1}); err == nil {
|
if _, err := dial.MetricsV1beta1().NodeMetricses().List(ctx, metav1.ListOptions{Limit: 1}); err == nil {
|
||||||
flag = true
|
metricsOK = true
|
||||||
} else {
|
} else {
|
||||||
log.Error().Err(err).Msgf("List metrics failed")
|
log.Error().Err(err).Msgf("List metrics failed")
|
||||||
}
|
}
|
||||||
a.cache.Add(cacheMXKey, flag, cacheExpiry)
|
|
||||||
|
|
||||||
return flag
|
return metricsOK
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial returns a handle to api server or die.
|
// Dial returns a handle to api server or die.
|
||||||
|
|
@ -412,10 +412,6 @@ func (a *APIClient) reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIClient) supportsMetricsResources() (supported bool, err error) {
|
func (a *APIClient) supportsMetricsResources() (supported bool, err error) {
|
||||||
defer func() {
|
|
||||||
a.cache.Add(cacheMXAPIKey, supported, cacheExpiry)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if v, ok := a.cache.Get(cacheMXAPIKey); ok {
|
if v, ok := a.cache.Get(cacheMXAPIKey); ok {
|
||||||
flag, k := v.(bool)
|
flag, k := v.(bool)
|
||||||
supported = k && flag
|
supported = k && flag
|
||||||
|
|
@ -424,6 +420,9 @@ func (a *APIClient) supportsMetricsResources() (supported bool, err error) {
|
||||||
if a.config == nil || a.config.flags == nil {
|
if a.config == nil || a.config.flags == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
a.cache.Add(cacheMXAPIKey, supported, cacheExpiry)
|
||||||
|
}()
|
||||||
|
|
||||||
dial, err := a.CachedDiscovery()
|
dial, err := a.CachedDiscovery()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,6 @@ func (c *Config) Load(path string) error {
|
||||||
|
|
||||||
// Save configuration to disk.
|
// Save configuration to disk.
|
||||||
func (c *Config) Save() error {
|
func (c *Config) Save() error {
|
||||||
log.Debug().Msg("[Config] Saving configuration...")
|
|
||||||
c.Validate()
|
c.Validate()
|
||||||
|
|
||||||
return c.SaveFile(K9sConfigFile)
|
return c.SaveFile(K9sConfigFile)
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ func TestSetup(t *testing.T) {
|
||||||
|
|
||||||
var expectedConfig = `k9s:
|
var expectedConfig = `k9s:
|
||||||
refreshRate: 100
|
refreshRate: 100
|
||||||
maxConnRetry: 15
|
maxConnRetry: 5
|
||||||
enableMouse: false
|
enableMouse: false
|
||||||
headless: false
|
headless: false
|
||||||
crumbsless: false
|
crumbsless: false
|
||||||
|
|
@ -344,7 +344,7 @@ var expectedConfig = `k9s:
|
||||||
|
|
||||||
var resetConfig = `k9s:
|
var resetConfig = `k9s:
|
||||||
refreshRate: 2
|
refreshRate: 2
|
||||||
maxConnRetry: 15
|
maxConnRetry: 5
|
||||||
enableMouse: false
|
enableMouse: false
|
||||||
headless: false
|
headless: false
|
||||||
crumbsless: false
|
crumbsless: false
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import "github.com/derailed/k9s/internal/client"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultRefreshRate = 2
|
defaultRefreshRate = 2
|
||||||
defaultMaxConnRetry = 15
|
defaultMaxConnRetry = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// K9s tracks K9s configuration options.
|
// K9s tracks K9s configuration options.
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ func (n *Node) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||||
)
|
)
|
||||||
if withMx, ok := ctx.Value(internal.KeyWithMetrics).(bool); withMx || !ok {
|
if withMx, ok := ctx.Value(internal.KeyWithMetrics).(bool); withMx || !ok {
|
||||||
if nmx, err = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx); err != nil {
|
if nmx, err = client.DialMetrics(n.Client()).FetchNodesMetrics(ctx); err != nil {
|
||||||
log.Warn().Err(err).Msgf("No node metrics")
|
log.Debug().Err(err).Msgf("No node metrics")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,6 @@ import (
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResourceViewerListener interface {
|
|
||||||
ResourceChanged(lines []string, matches fuzzy.Matches)
|
|
||||||
ResourceFailed(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ToggleOpts map[string]bool
|
|
||||||
|
|
||||||
type ResourceViewer interface {
|
|
||||||
GetPath() string
|
|
||||||
Filter(string)
|
|
||||||
ClearFilter()
|
|
||||||
Peek() []string
|
|
||||||
SetOptions(context.Context, ToggleOpts)
|
|
||||||
Watch(context.Context) error
|
|
||||||
AddListener(ResourceViewerListener)
|
|
||||||
RemoveListener(ResourceViewerListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe tracks describeable resources.
|
// Describe tracks describeable resources.
|
||||||
type Describe struct {
|
type Describe struct {
|
||||||
gvr client.GVR
|
gvr client.GVR
|
||||||
|
|
@ -50,7 +32,7 @@ func NewDescribe(gvr client.GVR, path string) *Describe {
|
||||||
return &Describe{
|
return &Describe{
|
||||||
gvr: gvr,
|
gvr: gvr,
|
||||||
path: path,
|
path: path,
|
||||||
refreshRate: 2 * time.Second,
|
refreshRate: defaultReaderRefreshRate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +42,7 @@ func (d *Describe) GetPath() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions toggle model options.
|
// SetOptions toggle model options.
|
||||||
func (d *Describe) SetOptions(context.Context, ToggleOpts) {}
|
func (d *Describe) SetOptions(context.Context, ViewerToggleOpts) {}
|
||||||
|
|
||||||
// Filter filters the model.
|
// Filter filters the model.
|
||||||
func (d *Describe) Filter(q string) {
|
func (d *Describe) Filter(q string) {
|
||||||
|
|
@ -134,27 +116,30 @@ func (d *Describe) Watch(ctx context.Context) error {
|
||||||
func (d *Describe) updater(ctx context.Context) {
|
func (d *Describe) updater(ctx context.Context) {
|
||||||
defer log.Debug().Msgf("Describe canceled -- %q", d.gvr)
|
defer log.Debug().Msgf("Describe canceled -- %q", d.gvr)
|
||||||
|
|
||||||
bf := backoff.NewExponentialBackOff()
|
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
|
||||||
bf.InitialInterval, bf.MaxElapsedTime = initRefreshRate, maxRetryInterval
|
delay := defaultReaderRefreshRate
|
||||||
rate := initRefreshRate
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(rate):
|
case <-time.After(delay):
|
||||||
rate = d.refreshRate
|
if err := d.refresh(ctx); err != nil {
|
||||||
err := backoff.Retry(func() error {
|
log.Error().Err(err).Msgf("Describe Failed")
|
||||||
return d.refresh(ctx)
|
|
||||||
}, backoff.WithContext(bf, ctx))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msgf("Retry failed")
|
|
||||||
d.fireResourceFailed(err)
|
d.fireResourceFailed(err)
|
||||||
return
|
delay = backOff.NextBackOff()
|
||||||
|
if delay == backoff.Stop {
|
||||||
|
log.Error().Err(err).Msgf("Describe done Retrying bailing out!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backOff.Reset()
|
||||||
|
delay = defaultReaderRefreshRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (d *Describe) refresh(ctx context.Context) error {
|
func (d *Describe) refresh(ctx context.Context) error {
|
||||||
|
log.Debug().Msgf("DESCRefresh %v", time.Now())
|
||||||
if !atomic.CompareAndSwapInt32(&d.inUpdate, 0, 1) {
|
if !atomic.CompareAndSwapInt32(&d.inUpdate, 0, 1) {
|
||||||
log.Debug().Msgf("Dropping update...")
|
log.Debug().Msgf("Dropping update...")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
runewidth "github.com/mattn/go-runewidth"
|
runewidth "github.com/mattn/go-runewidth"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
@ -23,3 +27,10 @@ func FQN(ns, n string) string {
|
||||||
func Truncate(str string, width int) string {
|
func Truncate(str string, width int) string {
|
||||||
return runewidth.Truncate(str, width, string(tview.SemigraphicsHorizontalEllipsis))
|
return runewidth.Truncate(str, width, string(tview.SemigraphicsHorizontalEllipsis))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewExpBackOff returns a new exponential backoff timer.
|
||||||
|
func NewExpBackOff(ctx context.Context, start, max time.Duration) backoff.BackOffContext {
|
||||||
|
bf := backoff.NewExponentialBackOff()
|
||||||
|
bf.InitialInterval, bf.MaxElapsedTime = start, max
|
||||||
|
return backoff.WithContext(bf, ctx)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ func (l *Log) Filter(q string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
l.filter = q
|
l.filter = q
|
||||||
|
// BOZO!! No needed since cmdbuff is now throttled!!
|
||||||
if l.filtering {
|
if l.filtering {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -352,7 +353,7 @@ func (l *Log) applyFilter(q string) ([][]byte, error) {
|
||||||
func (l *Log) fireLogBuffChanged(lines dao.LogItems) {
|
func (l *Log) fireLogBuffChanged(lines dao.LogItems) {
|
||||||
ll := make([][]byte, len(lines))
|
ll := make([][]byte, len(lines))
|
||||||
if l.filter == "" {
|
if l.filter == "" {
|
||||||
l.lines.Render(l.logOptions.ShowTimestamp, ll)
|
lines.Render(l.logOptions.ShowTimestamp, ll)
|
||||||
} else {
|
} else {
|
||||||
ff, err := l.applyFilter(l.filter)
|
ff, err := l.applyFilter(l.filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func (t *Table) updater(ctx context.Context) {
|
||||||
defer log.Debug().Msgf("TABLE-MODEL canceled -- %q", t.gvr)
|
defer log.Debug().Msgf("TABLE-MODEL canceled -- %q", t.gvr)
|
||||||
|
|
||||||
bf := backoff.NewExponentialBackOff()
|
bf := backoff.NewExponentialBackOff()
|
||||||
bf.InitialInterval, bf.MaxElapsedTime = initRefreshRate, maxRetryInterval
|
bf.InitialInterval, bf.MaxElapsedTime = initRefreshRate, maxReaderRetryInterval
|
||||||
rate := initRefreshRate
|
rate := initRefreshRate
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,42 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
"github.com/sahilm/fuzzy"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxReaderRetryInterval = 2 * time.Minute
|
||||||
|
defaultReaderRefreshRate = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceViewerListener listens to viewing resource events.
|
||||||
|
type ResourceViewerListener interface {
|
||||||
|
ResourceChanged(lines []string, matches fuzzy.Matches)
|
||||||
|
ResourceFailed(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToggleOpts represents a collection of viewing options.
|
||||||
|
type ViewerToggleOpts map[string]bool
|
||||||
|
|
||||||
|
// ResourceViewer represents a viewed resource.
|
||||||
|
type ResourceViewer interface {
|
||||||
|
GetPath() string
|
||||||
|
Filter(string)
|
||||||
|
ClearFilter()
|
||||||
|
Peek() []string
|
||||||
|
SetOptions(context.Context, ViewerToggleOpts)
|
||||||
|
Watch(context.Context) error
|
||||||
|
AddListener(ResourceViewerListener)
|
||||||
|
RemoveListener(ResourceViewerListener)
|
||||||
|
}
|
||||||
|
|
||||||
// Igniter represents a runnable view.
|
// Igniter represents a runnable view.
|
||||||
type Igniter interface {
|
type Igniter interface {
|
||||||
// Start starts a component.
|
// Start starts a component.
|
||||||
|
|
|
||||||
|
|
@ -18,31 +18,25 @@ import (
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// ManageFieldOpts tracks managed fields.
|
||||||
maxRetryInterval = 1 * time.Minute
|
const ManagedFieldsOpts = "ManagedFields"
|
||||||
|
|
||||||
// ManageFieldOpts tracks managed fields.
|
|
||||||
ManagedFieldsOpts = "ManagedFields"
|
|
||||||
)
|
|
||||||
|
|
||||||
// YAML tracks yaml resource representations.
|
// YAML tracks yaml resource representations.
|
||||||
type YAML struct {
|
type YAML struct {
|
||||||
gvr client.GVR
|
gvr client.GVR
|
||||||
inUpdate int32
|
inUpdate int32
|
||||||
path string
|
path string
|
||||||
query string
|
query string
|
||||||
lines []string
|
lines []string
|
||||||
refreshRate time.Duration
|
listeners []ResourceViewerListener
|
||||||
listeners []ResourceViewerListener
|
options ViewerToggleOpts
|
||||||
options ToggleOpts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYAML return a new yaml resource model.
|
// NewYAML return a new yaml resource model.
|
||||||
func NewYAML(gvr client.GVR, path string) *YAML {
|
func NewYAML(gvr client.GVR, path string) *YAML {
|
||||||
return &YAML{
|
return &YAML{
|
||||||
gvr: gvr,
|
gvr: gvr,
|
||||||
path: path,
|
path: path,
|
||||||
refreshRate: 2 * time.Second,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +46,7 @@ func (y *YAML) GetPath() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions toggle model options.
|
// SetOptions toggle model options.
|
||||||
func (y *YAML) SetOptions(ctx context.Context, opts ToggleOpts) {
|
func (y *YAML) SetOptions(ctx context.Context, opts ViewerToggleOpts) {
|
||||||
y.options = opts
|
y.options = opts
|
||||||
if err := y.refresh(ctx); err != nil {
|
if err := y.refresh(ctx); err != nil {
|
||||||
y.fireResourceFailed(err)
|
y.fireResourceFailed(err)
|
||||||
|
|
@ -133,28 +127,31 @@ func (y *YAML) Watch(ctx context.Context) error {
|
||||||
func (y *YAML) updater(ctx context.Context) {
|
func (y *YAML) updater(ctx context.Context) {
|
||||||
defer log.Debug().Msgf("YAML canceled -- %q", y.gvr)
|
defer log.Debug().Msgf("YAML canceled -- %q", y.gvr)
|
||||||
|
|
||||||
bf := backoff.NewExponentialBackOff()
|
backOff := NewExpBackOff(ctx, defaultReaderRefreshRate, maxReaderRetryInterval)
|
||||||
bf.InitialInterval, bf.MaxElapsedTime = initRefreshRate, maxRetryInterval
|
delay := defaultReaderRefreshRate
|
||||||
rate := initRefreshRate
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(rate):
|
case <-time.After(delay):
|
||||||
rate = y.refreshRate
|
if err := y.refresh(ctx); err != nil {
|
||||||
err := backoff.Retry(func() error {
|
log.Error().Err(err).Msgf("YAML Failed")
|
||||||
return y.refresh(ctx)
|
|
||||||
}, backoff.WithContext(bf, ctx))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msgf("Retry failed")
|
|
||||||
y.fireResourceFailed(err)
|
y.fireResourceFailed(err)
|
||||||
return
|
delay = backOff.NextBackOff()
|
||||||
|
if delay == backoff.Stop {
|
||||||
|
log.Error().Err(err).Msgf("YAML done Retrying bailing out!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backOff.Reset()
|
||||||
|
delay = defaultReaderRefreshRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (y *YAML) refresh(ctx context.Context) error {
|
func (y *YAML) refresh(ctx context.Context) error {
|
||||||
|
log.Debug().Msgf("YAMLRefresh %v", time.Now())
|
||||||
if !atomic.CompareAndSwapInt32(&y.inUpdate, 0, 1) {
|
if !atomic.CompareAndSwapInt32(&y.inUpdate, 0, 1) {
|
||||||
log.Debug().Msgf("Dropping update...")
|
log.Debug().Msgf("Dropping update...")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff"
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
|
@ -273,19 +274,35 @@ func (a *App) Resume() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) clusterUpdater(ctx context.Context) {
|
func (a *App) clusterUpdater(ctx context.Context) {
|
||||||
a.refreshCluster()
|
if err := a.refreshCluster(); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Cluster updater failed!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bf := model.NewExpBackOff(ctx, clusterRefresh, 2*time.Minute)
|
||||||
|
delay := clusterRefresh
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Debug().Msg("ClusterInfo updater canceled!")
|
log.Debug().Msg("ClusterInfo updater canceled!")
|
||||||
return
|
return
|
||||||
case <-time.After(clusterRefresh):
|
case <-time.After(delay):
|
||||||
a.refreshCluster()
|
if err := a.refreshCluster(); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("ClusterUpdater failed")
|
||||||
|
if delay = bf.NextBackOff(); delay == backoff.Stop {
|
||||||
|
a.BailOut()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf.Reset()
|
||||||
|
delay = clusterRefresh
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) refreshCluster() {
|
func (a *App) refreshCluster() error {
|
||||||
|
log.Debug().Msgf("Cluster Refresh %v", time.Now())
|
||||||
c := a.Content.Top()
|
c := a.Content.Top()
|
||||||
if ok := a.Conn().CheckConnectivity(); ok {
|
if ok := a.Conn().CheckConnectivity(); ok {
|
||||||
if atomic.LoadInt32(&a.conRetry) > 0 {
|
if atomic.LoadInt32(&a.conRetry) > 0 {
|
||||||
|
|
@ -305,13 +322,13 @@ func (a *App) refreshCluster() {
|
||||||
|
|
||||||
count, maxConnRetry := atomic.LoadInt32(&a.conRetry), int32(a.Config.K9s.MaxConnRetry)
|
count, maxConnRetry := atomic.LoadInt32(&a.conRetry), int32(a.Config.K9s.MaxConnRetry)
|
||||||
if count >= maxConnRetry {
|
if count >= maxConnRetry {
|
||||||
|
log.Error().Msgf("Conn check failed (%d/%d). Bailing out!", count, maxConnRetry)
|
||||||
ExitStatus = fmt.Sprintf("Lost K8s connection (%d). Bailing out!", count)
|
ExitStatus = fmt.Sprintf("Lost K8s connection (%d). Bailing out!", count)
|
||||||
a.BailOut()
|
a.BailOut()
|
||||||
}
|
}
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
log.Warn().Msgf("Conn check failed (%d/%d)", count, maxConnRetry)
|
a.Status(model.FlashWarn, fmt.Sprintf("Dial K8s Toast [%d/%d]", count, maxConnRetry))
|
||||||
a.Status(model.FlashWarn, fmt.Sprintf("Dial K8s failed (%d)", count))
|
return fmt.Errorf("Conn check failed (%d/%d)", count, maxConnRetry)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload alias
|
// Reload alias
|
||||||
|
|
@ -323,6 +340,8 @@ func (a *App) refreshCluster() {
|
||||||
|
|
||||||
// Update cluster info
|
// Update cluster info
|
||||||
a.clusterModel.Refresh()
|
a.clusterModel.Refresh()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) switchNS(ns string) error {
|
func (a *App) switchNS(ns string) error {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func (c *Cow) talk() {
|
||||||
func cowTalk(says string) string {
|
func cowTalk(says string) string {
|
||||||
buff := make([]string, 0, len(cow)+3)
|
buff := make([]string, 0, len(cow)+3)
|
||||||
buff = append(buff, " "+strings.Repeat("─", len(says)+8))
|
buff = append(buff, " "+strings.Repeat("─", len(says)+8))
|
||||||
buff = append(buff, fmt.Sprintf("< [red::b]Ruroh? %s [-::-] >", says))
|
buff = append(buff, fmt.Sprintf("< [red::b]Ruroh? %s[-::-] >", says))
|
||||||
buff = append(buff, " "+strings.Repeat("─", len(says)+8))
|
buff = append(buff, " "+strings.Repeat("─", len(says)+8))
|
||||||
spacer := strings.Repeat(" ", len(says)/2-8)
|
spacer := strings.Repeat(" ", len(says)/2-8)
|
||||||
for _, s := range cow {
|
for _, s := range cow {
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ func (v *LiveView) ResourceFailed(err error) {
|
||||||
// ResourceChanged notifies when the filter changes.
|
// ResourceChanged notifies when the filter changes.
|
||||||
func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) {
|
func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||||
v.app.QueueUpdateDraw(func() {
|
v.app.QueueUpdateDraw(func() {
|
||||||
|
v.text.SetTextAlign(tview.AlignLeft)
|
||||||
v.maxRegions = len(matches)
|
v.maxRegions = len(matches)
|
||||||
ll := make([]string, len(lines))
|
ll := make([]string, len(lines))
|
||||||
copy(ll, lines)
|
copy(ll, lines)
|
||||||
|
|
@ -96,9 +97,10 @@ func (v *LiveView) ResourceChanged(lines []string, matches fuzzy.Matches) {
|
||||||
ll[m.Index] = line[:loc[0]] + `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>` + line[loc[1]:]
|
ll[m.Index] = line[:loc[0]] + `<<<"search_` + strconv.Itoa(i) + `">>>` + line[loc[0]:loc[1]] + `<<<"">>>` + line[loc[1]:]
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.maxRegions == 0 {
|
if v.text.GetText(true) == "" {
|
||||||
v.text.ScrollToBeginning()
|
v.text.ScrollToBeginning()
|
||||||
}
|
}
|
||||||
|
|
||||||
v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
|
v.text.SetText(colorizeYAML(v.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
|
||||||
v.text.Highlight()
|
v.text.Highlight()
|
||||||
if v.currentRegion < v.maxRegions {
|
if v.currentRegion < v.maxRegions {
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ func (l *Log) bindKeys() {
|
||||||
ui.Key5: ui.NewKeyAction("1h", l.sinceCmd(60*60), true),
|
ui.Key5: ui.NewKeyAction("1h", l.sinceCmd(60*60), true),
|
||||||
tcell.KeyEnter: ui.NewSharedKeyAction("Filter", l.filterCmd, false),
|
tcell.KeyEnter: ui.NewSharedKeyAction("Filter", l.filterCmd, false),
|
||||||
tcell.KeyEscape: ui.NewKeyAction("Back", l.resetCmd, false),
|
tcell.KeyEscape: ui.NewKeyAction("Back", l.resetCmd, false),
|
||||||
tcell.KeyCtrlK: ui.NewKeyAction("Clear", l.clearCmd, true),
|
ui.KeyShiftC: ui.NewKeyAction("Clear", l.clearCmd, true),
|
||||||
ui.KeyM: ui.NewKeyAction("Mark", l.markCmd, true),
|
ui.KeyM: ui.NewKeyAction("Mark", l.markCmd, true),
|
||||||
ui.KeyS: ui.NewKeyAction("Toggle AutoScroll", l.toggleAutoScrollCmd, true),
|
ui.KeyS: ui.NewKeyAction("Toggle AutoScroll", l.toggleAutoScrollCmd, true),
|
||||||
ui.KeyF: ui.NewKeyAction("Toggle FullScreen", l.toggleFullScreenCmd, true),
|
ui.KeyF: ui.NewKeyAction("Toggle FullScreen", l.toggleFullScreenCmd, true),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue