checkpoint

mine
derailed 2019-12-29 12:53:16 -07:00
parent 365fc01f17
commit 5c0fc0845b
13 changed files with 112 additions and 81 deletions

View File

@ -3,6 +3,7 @@ package client
import ( import (
"errors" "errors"
"fmt" "fmt"
"sync"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
@ -19,11 +20,15 @@ type Config struct {
currentContext string currentContext string
rawConfig *clientcmdapi.Config rawConfig *clientcmdapi.Config
restConfig *restclient.Config restConfig *restclient.Config
mutex *sync.RWMutex
} }
// NewConfig returns a new k8s config or an error if the flags are invalid. // NewConfig returns a new k8s config or an error if the flags are invalid.
func NewConfig(f *genericclioptions.ConfigFlags) *Config { func NewConfig(f *genericclioptions.ConfigFlags) *Config {
return &Config{flags: f} return &Config{
flags: f,
mutex: &sync.RWMutex{},
}
} }
// Flags returns configuration flags. // Flags returns configuration flags.
@ -231,12 +236,18 @@ func (c *Config) NamespaceNames(nns []v1.Namespace) []string {
// ConfigAccess return the current kubeconfig api server access configuration. // ConfigAccess return the current kubeconfig api server access configuration.
func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) { func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) {
c.mutex.RLock()
defer c.mutex.RUnlock()
c.ensureConfig() c.ensureConfig()
return c.clientConfig.ConfigAccess(), nil return c.clientConfig.ConfigAccess(), nil
} }
// RawConfig fetch the current kubeconfig with no overrides. // RawConfig fetch the current kubeconfig with no overrides.
func (c *Config) RawConfig() (clientcmdapi.Config, error) { func (c *Config) RawConfig() (clientcmdapi.Config, error) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.rawConfig != nil { if c.rawConfig != nil {
if c.rawConfig.CurrentContext == c.currentContext { if c.rawConfig.CurrentContext == c.currentContext {
return *c.rawConfig, nil return *c.rawConfig, nil

View File

@ -22,7 +22,7 @@ type TableListener interface {
type Table struct { type Table struct {
gvr string gvr string
namespace string namespace string
data render.TableData data *render.TableData
listeners []TableListener listeners []TableListener
inUpdate int32 inUpdate int32
refreshRate time.Duration refreshRate time.Duration
@ -32,7 +32,7 @@ type Table struct {
func NewTable(gvr string) *Table { func NewTable(gvr string) *Table {
return &Table{ return &Table{
gvr: gvr, gvr: gvr,
data: render.TableData{}, data: render.NewTableData(),
refreshRate: 2 * time.Second, refreshRate: 2 * time.Second,
} }
} }
@ -81,7 +81,7 @@ func (t *Table) Empty() bool {
// Peek returns model data. // Peek returns model data.
func (t *Table) Peek() render.TableData { func (t *Table) Peek() render.TableData {
return t.data return *t.data
} }
func (t *Table) updater(ctx context.Context) { func (t *Table) updater(ctx context.Context) {
@ -107,13 +107,13 @@ func (t *Table) refresh(ctx context.Context) {
log.Error().Err(err).Msg("Reconcile failed") log.Error().Err(err).Msg("Reconcile failed")
t.fireTableLoadFailed(err) t.fireTableLoadFailed(err)
} }
t.fireTableChanged(t.data) t.fireTableChanged(*t.data)
} }
// 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) t.fireTableChanged(*t.data)
} }
// RemoveListener delete a listener from the list. // RemoveListener delete a listener from the list.
@ -144,6 +144,9 @@ func (t *Table) fireTableLoadFailed(err error) {
} }
func (t *Table) reconcile(ctx context.Context) error { func (t *Table) reconcile(ctx context.Context) error {
t.data.Mutex.Lock()
defer t.data.Mutex.Unlock()
log.Debug().Msgf("GOROUTINE %d", runtime.NumGoroutine()) log.Debug().Msgf("GOROUTINE %d", runtime.NumGoroutine())
factory, ok := ctx.Value(internal.KeyFactory).(Factory) factory, ok := ctx.Value(internal.KeyFactory).(Factory)

View File

@ -1,10 +1,18 @@
package render package render
import "sync"
// TableData tracks a K8s resource for tabular display. // TableData tracks a K8s resource for tabular display.
type TableData struct { type TableData struct {
Header HeaderRow Header HeaderRow
RowEvents RowEvents RowEvents RowEvents
Namespace string Namespace string
Mutex *sync.RWMutex
}
// NewTableData returns a new table.
func NewTableData() *TableData {
return &TableData{Mutex: &sync.RWMutex{}}
} }
// Clear clears out the entire table. // Clear clears out the entire table.

View File

@ -134,6 +134,9 @@ func (t *Table) SetSortCol(index, count int, asc bool) {
// Update table content. // Update table content.
func (t *Table) Update(data render.TableData) { func (t *Table) Update(data render.TableData) {
data.Mutex.RLock()
defer data.Mutex.RUnlock()
var firstRow bool var firstRow bool
if t.GetRowCount() == 0 { if t.GetRowCount() == 0 {
firstRow = true firstRow = true

View File

@ -69,14 +69,14 @@ func (t *testModel) InNamespace(string) bool { return true }
func (t *testModel) SetRefreshRate(time.Duration) {} func (t *testModel) SetRefreshRate(time.Duration) {}
func makeTableData() render.TableData { func makeTableData() render.TableData {
return render.TableData{ t := render.NewTableData()
Namespace: "", t.Namespace = ""
Header: render.HeaderRow{ t.Header = render.HeaderRow{
render.Header{Name: "a"}, render.Header{Name: "a"},
render.Header{Name: "b"}, render.Header{Name: "b"},
render.Header{Name: "c"}, render.Header{Name: "c"},
}, }
RowEvents: render.RowEvents{ t.RowEvents = render.RowEvents{
render.RowEvent{ render.RowEvent{
Row: render.Row{ Row: render.Row{
ID: "r1", ID: "r1",
@ -89,6 +89,7 @@ func makeTableData() render.TableData {
Fields: render.Fields{"blee", "duh", "zorg"}, Fields: render.Fields{"blee", "duh", "zorg"},
}, },
}, },
},
} }
return *t
} }

View File

@ -193,7 +193,9 @@ func (a *App) Resume() {
var ctx context.Context var ctx context.Context
ctx, a.cancelFn = context.WithCancel(context.Background()) ctx, a.cancelFn = context.WithCancel(context.Background())
go a.clusterUpdater(ctx) go a.clusterUpdater(ctx)
a.StylesUpdater(ctx, a) if err := a.StylesUpdater(ctx, a); err != nil {
log.Error().Err(err).Msgf("Styles update failed")
}
} }
func (a *App) clusterUpdater(ctx context.Context) { func (a *App) clusterUpdater(ctx context.Context) {

View File

@ -1,6 +1,8 @@
package view package view
import ( import (
"strings"
"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/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -52,7 +54,7 @@ func (d *Deploy) showPods(app *App, _, _, path string) {
app.Flash().Err(err) app.Flash().Err(err)
} }
showPodsFromSelector(app, path, dp.Spec.Selector) showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), dp.Spec.Selector)
} }
// Helpers... // Helpers...

View File

@ -1,6 +1,8 @@
package view package view
import ( import (
"strings"
"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/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -47,5 +49,5 @@ func (d *DaemonSet) showPods(app *App, _, _, path string) {
d.App().Flash().Err(err) d.App().Flash().Err(err)
} }
showPodsFromSelector(app, path, ds.Spec.Selector) showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), ds.Spec.Selector)
} }

View File

@ -41,7 +41,6 @@ type Log struct {
cancelFn context.CancelFunc cancelFn context.CancelFunc
previous bool previous bool
gvr client.GVR gvr client.GVR
fullScreen bool
} }
var _ model.Component = &Log{} var _ model.Component = &Log{}

View File

@ -39,7 +39,7 @@ func (l *LogsExtender) logsCmd(prev bool) func(evt *tcell.EventKey) *tcell.Event
if path == "" { if path == "" {
return nil return nil
} }
if l.GetTable().Path != "" { if isResourcePath(l.GetTable().Path) {
path = l.GetTable().Path path = l.GetTable().Path
} }
l.showLogs(path, prev) l.showLogs(path, prev)
@ -48,11 +48,15 @@ func (l *LogsExtender) logsCmd(prev bool) func(evt *tcell.EventKey) *tcell.Event
} }
} }
func isResourcePath(p string) bool {
ns, n := client.Namespaced(p)
return ns != "" && n != ""
}
func (l *LogsExtender) showLogs(path string, prev bool) { func (l *LogsExtender) showLogs(path string, prev bool) {
log.Debug().Msgf("SHOWING LOGS path %q", path) log.Debug().Msgf("SHOWING LOGS path %q", path)
co := "" co := ""
if l.containerFn != nil { if l.containerFn != nil {
log.Debug().Msgf("CUSTOM CO FUNC")
co = l.containerFn() co = l.containerFn()
} }
if err := l.App().inject(NewLog(client.GVR(l.GVR()), path, co, prev)); err != nil { if err := l.App().inject(NewLog(client.GVR(l.GVR()), path, co, prev)); err != nil {

View File

@ -1,6 +1,8 @@
package view package view
import ( import (
"strings"
"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/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
@ -51,5 +53,5 @@ func (s *StatefulSet) showPods(app *App, _, gvr, path string) {
app.Flash().Err(err) app.Flash().Err(err)
} }
showPodsFromSelector(app, path, sts.Spec.Selector) showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), sts.Spec.Selector)
} }

View File

@ -33,14 +33,14 @@ func TestTableNew(t *testing.T) {
v := NewTable("test") v := NewTable("test")
v.Init(makeContext()) v.Init(makeContext())
data := render.TableData{ data := render.NewTableData()
Header: render.HeaderRow{ data.Header = render.HeaderRow{
render.Header{Name: "NAMESPACE"}, render.Header{Name: "NAMESPACE"},
render.Header{Name: "NAME", Align: tview.AlignRight}, render.Header{Name: "NAME", Align: tview.AlignRight},
render.Header{Name: "FRED"}, render.Header{Name: "FRED"},
render.Header{Name: "AGE", Decorator: render.AgeDecorator}, render.Header{Name: "AGE", Decorator: render.AgeDecorator},
}, }
RowEvents: render.RowEvents{ data.RowEvents = render.RowEvents{
render.RowEvent{ render.RowEvent{
Row: render.Row{ Row: render.Row{
Fields: render.Fields{"ns1", "a", "10", "3m"}, Fields: render.Fields{"ns1", "a", "10", "3m"},
@ -51,10 +51,10 @@ func TestTableNew(t *testing.T) {
Fields: render.Fields{"ns1", "b", "15", "1m"}, Fields: render.Fields{"ns1", "b", "15", "1m"},
}, },
}, },
},
Namespace: "",
} }
v.Update(data) data.Namespace = ""
v.Update(*data)
assert.Equal(t, 3, v.GetRowCount()) assert.Equal(t, 3, v.GetRowCount())
} }
@ -101,14 +101,15 @@ func (t *testTableModel) InNamespace(string) bool { return true }
func (t *testTableModel) SetRefreshRate(time.Duration) {} func (t *testTableModel) SetRefreshRate(time.Duration) {}
func makeTableData() render.TableData { func makeTableData() render.TableData {
return render.TableData{ t := render.NewTableData()
Header: render.HeaderRow{
t.Header = render.HeaderRow{
render.Header{Name: "NAMESPACE"}, render.Header{Name: "NAMESPACE"},
render.Header{Name: "NAME", Align: tview.AlignRight}, render.Header{Name: "NAME", Align: tview.AlignRight},
render.Header{Name: "FRED"}, render.Header{Name: "FRED"},
render.Header{Name: "AGE", Decorator: render.AgeDecorator}, render.Header{Name: "AGE", Decorator: render.AgeDecorator},
}, }
RowEvents: render.RowEvents{ t.RowEvents = render.RowEvents{
render.RowEvent{ render.RowEvent{
Row: render.Row{ Row: render.Row{
Fields: render.Fields{"ns1", "blee", "10", "3m"}, Fields: render.Fields{"ns1", "blee", "10", "3m"},
@ -120,9 +121,10 @@ func makeTableData() render.TableData {
}, },
Deltas: render.DeltaRow{"", "", "20", ""}, Deltas: render.DeltaRow{"", "", "20", ""},
}, },
},
Namespace: "",
} }
t.Namespace = ""
return *t
} }
func makeContext() context.Context { func makeContext() context.Context {

View File

@ -8,9 +8,6 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
"net/http"
_ "net/http/pprof"
) )
func init() { func init() {
@ -23,11 +20,6 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file}) log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
cmd.Execute() cmd.Execute()