derailed 2020-05-29 13:23:42 -06:00
parent 3a02001695
commit 19a4594eea
8 changed files with 89 additions and 37 deletions

View File

@ -0,0 +1,36 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.20.2
## Notes
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better is as ever very much noticed and appreciated!
Also if you dig this tool, consider joining our [sponsorhip program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
---
Maintenance Release! Fixing a few issue in the v0.20 aftermath ;(
## Selection Marker
In this drop, we're adding the ability to set row marks ranges. There are situations where you've filtered a resource and need to delete part or all of the rows. In previous releases, you had to mark each rows one by one. Now you have the ability to select a beginning and an end range and all rows in between will now be marked! To mark a single row, you can use `space`. To select rows between your initial mark to the current selection use `Ctrl-space`. To nuke all marked rows use `Ctrl-\`. All credits and ATTA BOY goes to [Ryan Richard](https://github.com/cfryanr) for suggesting this feature!!
## Logs Got Some TLC!
Per [Raman Gupta](https://github.com/rocketraman) excellent suggestion, we've added a way to add a separator to your chatty logs to easily see the latest incoming logs. While in log view you can now press `m` to add the separator to the log stream. If you don't care about the log history and just want to see the latest incoming logs, pressing `c` will clear out the log viewer.
## Resolved Bugs/Features/PRs
- [Issue #741](https://github.com/derailed/k9s/issues/741)
- [Issue #740](https://github.com/derailed/k9s/issues/740)
- [Issue #739](https://github.com/derailed/k9s/issues/739)
- [Issue #727](https://github.com/derailed/k9s/issues/727)
- [Issue #723](https://github.com/derailed/k9s/issues/723)
- [PR #725](https://github.com/derailed/k9s/pull/725) Big Thanks To [Soupyt](https://github.com/soupyt)!!
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -135,7 +135,9 @@ func (a *APIClient) clearCache() {
// CanI checks if user has access to a certain resource.
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
log.Debug().Msgf("Check Access %q::%q", ns, gvr)
a.mx.Lock()
defer a.mx.Unlock()
if !a.connOK {
return false, errors.New("ACCESS -- No API server connection")
}
@ -149,7 +151,6 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error)
}
}
log.Debug().Msgf("----> Calling API")
dial, err := a.Dial()
if err != nil {
return false, err
@ -171,9 +172,9 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error)
return auth, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr)
}
}
auth = true
a.cache.Add(key, true, cacheExpiry)
return
}
@ -214,20 +215,22 @@ func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
}
// CheckConnectivity return true if api server is cool or false otherwise.
func (a *APIClient) CheckConnectivity() (status bool) {
func (a *APIClient) CheckConnectivity() (ok bool) {
a.mx.Lock()
defer a.mx.Unlock()
defer func() {
if err := recover(); err != nil {
status = false
ok = false
}
if !status {
if !ok {
a.clearCache()
}
a.connOK = status
a.connOK = ok
}()
// Need to reload to pickup any kubeconfig changes.
config := NewConfig(a.config.flags)
cfg, err := config.RESTConfig()
cfg, err := NewConfig(a.config.flags).RESTConfig()
if err != nil {
return false
}
@ -237,7 +240,6 @@ func (a *APIClient) CheckConnectivity() (status bool) {
log.Error().Err(err).Msgf("Unable to connect to api server")
return
}
log.Debug().Msgf("CONN-CHECK on %#v", cfg.Host)
// Check connection
if _, err := client.ServerVersion(); err == nil {
@ -245,7 +247,7 @@ func (a *APIClient) CheckConnectivity() (status bool) {
log.Debug().Msgf("RESETING CON!!")
a.reset()
}
status = true
ok = true
} else {
log.Error().Err(err).Msgf("K9s can't connect to cluster")
}

View File

@ -38,6 +38,7 @@ type Log struct {
filter string
lastSent int
flushTimeout time.Duration
filtering bool
}
// NewLog returns a new model.
@ -52,6 +53,9 @@ func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Lo
// LogOptions returns the current log options.
func (l *Log) LogOptions() dao.LogOptions {
l.mx.RLock()
defer l.mx.RUnlock()
return l.logOptions
}
@ -59,12 +63,18 @@ func (l *Log) LogOptions() dao.LogOptions {
func (l *Log) SinceSeconds() int64 {
l.mx.RLock()
defer l.mx.RUnlock()
return l.logOptions.SinceSeconds
}
// SetLogOptions updates logger options.
func (l *Log) SetLogOptions(opts dao.LogOptions) {
l.logOptions = opts
l.mx.Lock()
{
l.logOptions = opts
}
l.mx.Unlock()
l.Restart()
}
@ -96,6 +106,7 @@ func (l *Log) Clear() {
l.lines, l.lastSent = dao.LogItems{}, 0
}
l.mx.Unlock()
l.fireLogCleared()
}
@ -148,16 +159,25 @@ func (l *Log) ClearFilter() {
l.fireLogChanged(l.lines)
}
// Filter filters the model using either fuzzy or regexp.
func (l *Log) Filter(q string) error {
l.mx.RLock()
defer l.mx.RUnlock()
// Filter filters th:e model using either fuzzy or regexp.
func (l *Log) Filter(q string) {
log.Debug().Msgf("Filter %q", q)
l.mx.Lock()
defer l.mx.Unlock()
l.filter = q
l.fireLogCleared()
l.fireLogBuffChanged(l.lines)
return nil
if l.filtering {
return
}
l.filtering = true
go func(l *Log) {
<-time.After(500 * time.Millisecond)
l.mx.Lock()
defer l.mx.Unlock()
l.fireLogCleared()
l.fireLogBuffChanged(l.lines)
l.filtering = false
}(l)
}
func (l *Log) load() error {

View File

@ -75,13 +75,13 @@ func TestLogFilter(t *testing.T) {
m.Notify(true)
assert.Equal(t, 1, v.dataCalled)
assert.Equal(t, 2, v.clearCalled)
assert.Equal(t, 1, v.clearCalled)
assert.Equal(t, 0, v.errCalled)
assert.Equal(t, u.e, len(v.data))
m.ClearFilter()
assert.Equal(t, 2, v.dataCalled)
assert.Equal(t, 3, v.clearCalled)
assert.Equal(t, 2, v.clearCalled)
assert.Equal(t, 0, v.errCalled)
assert.Equal(t, size, len(v.data))
})
@ -193,7 +193,7 @@ func TestLogTimedout(t *testing.T) {
}
m.Notify(true)
assert.Equal(t, 1, v.dataCalled)
assert.Equal(t, 2, v.clearCalled)
assert.Equal(t, 1, v.clearCalled)
assert.Equal(t, 0, v.errCalled)
const e = "\x1b[38;5;209ml\x1b[0m\x1b[38;5;209mi\x1b[0m\x1b[38;5;209mn\x1b[0m\x1b[38;5;209me\x1b[0m\x1b[38;5;209m1\x1b[0m"
assert.Equal(t, e, string(v.data[0].Bytes))

View File

@ -142,7 +142,6 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
func (c *Command) defaultCmd() error {
if !c.app.Conn().ConnectionOK() {
log.Debug().Msgf("YO!!")
return c.run("ctx", "", true)
}
view := c.app.Config.ActiveView()

View File

@ -26,7 +26,7 @@ const (
logMessage = "Waiting for logs..."
logFmt = " Logs([hilite:bg:]%s[-:bg:-])[[green:bg:b]%s[-:bg:-]] "
logCoFmt = " Logs([hilite:bg:]%s:[hilite:bg:b]%s[-:bg:-])[[green:bg:b]%s[-:bg:-]] "
flushTimeout = 100 * time.Millisecond
flushTimeout = 50 * time.Millisecond
)
// Log represents a generic log viewer.
@ -123,9 +123,7 @@ func (l *Log) LogChanged(lines dao.LogItems) {
// BufferChanged indicates the buffer was changed.
func (l *Log) BufferChanged(s string) {
if err := l.model.Filter(l.logs.cmdBuff.GetText()); err != nil {
l.app.Flash().Err(err)
}
l.model.Filter(l.logs.cmdBuff.GetText())
l.updateTitle()
}
@ -268,10 +266,9 @@ func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.logs.cmdBuff.IsActive() {
return evt
}
l.logs.cmdBuff.SetActive(false)
if err := l.model.Filter(l.logs.cmdBuff.GetText()); err != nil {
l.app.Flash().Err(err)
}
l.model.Filter(l.logs.cmdBuff.GetText())
l.updateTitle()
return nil
@ -326,7 +323,7 @@ func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
}
func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey {
fmt.Fprintln(l.ansiWriter, fmt.Sprintf("[white::b]%s[::]", strings.Repeat("-", 80)))
fmt.Fprintln(l.ansiWriter, fmt.Sprintf("[white::b]%s[::]", strings.Repeat("", 80)))
return nil
}

View File

@ -85,9 +85,9 @@ func TestLogFilter(t *testing.T) {
l.SendKeys(ui.KeySlash)
l.SendStrokes("zorg")
assert.Equal(t, "\x1b[38;5;209mz\x1b[0m\x1b[38;5;209mo\x1b[0m\x1b[38;5;209mr\x1b[0m\x1b[38;5;209mg\x1b[0m", list.lines)
assert.Equal(t, 5, list.change)
assert.Equal(t, 5, list.clear)
assert.Equal(t, "duhzorg", list.lines)
assert.Equal(t, 1, list.change)
assert.Equal(t, 1, list.clear)
assert.Equal(t, 0, list.fail)
}

View File

@ -70,7 +70,6 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
if err != nil {
return nil, err
}
log.Debug().Msgf("LIST %q::%q -- %t::%t", gvr, ns, wait, inf.Informer().HasSynced())
if client.IsAllNamespace(ns) {
ns = client.AllNamespaces
}
@ -109,7 +108,6 @@ func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime
if err != nil {
return nil, err
}
log.Debug().Msgf("GET %q::%q -- %t::%t", gvr, path, wait, inf.Informer().HasSynced())
var o runtime.Object
if client.IsClusterScoped(ns) {
o, err = inf.Lister().Get(n)