checkpoint
parent
365fc01f17
commit
5c0fc0845b
|
|
@ -3,6 +3,7 @@ package client
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -19,11 +20,15 @@ type Config struct {
|
|||
currentContext string
|
||||
rawConfig *clientcmdapi.Config
|
||||
restConfig *restclient.Config
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewConfig returns a new k8s config or an error if the flags are invalid.
|
||||
func NewConfig(f *genericclioptions.ConfigFlags) *Config {
|
||||
return &Config{flags: f}
|
||||
return &Config{
|
||||
flags: f,
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
c.ensureConfig()
|
||||
return c.clientConfig.ConfigAccess(), nil
|
||||
}
|
||||
|
||||
// RawConfig fetch the current kubeconfig with no overrides.
|
||||
func (c *Config) RawConfig() (clientcmdapi.Config, error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.rawConfig != nil {
|
||||
if c.rawConfig.CurrentContext == c.currentContext {
|
||||
return *c.rawConfig, nil
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type TableListener interface {
|
|||
type Table struct {
|
||||
gvr string
|
||||
namespace string
|
||||
data render.TableData
|
||||
data *render.TableData
|
||||
listeners []TableListener
|
||||
inUpdate int32
|
||||
refreshRate time.Duration
|
||||
|
|
@ -32,7 +32,7 @@ type Table struct {
|
|||
func NewTable(gvr string) *Table {
|
||||
return &Table{
|
||||
gvr: gvr,
|
||||
data: render.TableData{},
|
||||
data: render.NewTableData(),
|
||||
refreshRate: 2 * time.Second,
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ func (t *Table) Empty() bool {
|
|||
|
||||
// Peek returns model data.
|
||||
func (t *Table) Peek() render.TableData {
|
||||
return t.data
|
||||
return *t.data
|
||||
}
|
||||
|
||||
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")
|
||||
t.fireTableLoadFailed(err)
|
||||
}
|
||||
t.fireTableChanged(t.data)
|
||||
t.fireTableChanged(*t.data)
|
||||
}
|
||||
|
||||
// AddListener adds a new model listener.
|
||||
func (t *Table) AddListener(l TableListener) {
|
||||
t.listeners = append(t.listeners, l)
|
||||
t.fireTableChanged(t.data)
|
||||
t.fireTableChanged(*t.data)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
t.data.Mutex.Lock()
|
||||
defer t.data.Mutex.Unlock()
|
||||
|
||||
log.Debug().Msgf("GOROUTINE %d", runtime.NumGoroutine())
|
||||
|
||||
factory, ok := ctx.Value(internal.KeyFactory).(Factory)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
package render
|
||||
|
||||
import "sync"
|
||||
|
||||
// TableData tracks a K8s resource for tabular display.
|
||||
type TableData struct {
|
||||
Header HeaderRow
|
||||
RowEvents RowEvents
|
||||
Namespace string
|
||||
Mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewTableData returns a new table.
|
||||
func NewTableData() *TableData {
|
||||
return &TableData{Mutex: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// Clear clears out the entire table.
|
||||
|
|
|
|||
|
|
@ -134,6 +134,9 @@ func (t *Table) SetSortCol(index, count int, asc bool) {
|
|||
|
||||
// Update table content.
|
||||
func (t *Table) Update(data render.TableData) {
|
||||
data.Mutex.RLock()
|
||||
defer data.Mutex.RUnlock()
|
||||
|
||||
var firstRow bool
|
||||
if t.GetRowCount() == 0 {
|
||||
firstRow = true
|
||||
|
|
|
|||
|
|
@ -69,26 +69,27 @@ func (t *testModel) InNamespace(string) bool { return true }
|
|||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||
|
||||
func makeTableData() render.TableData {
|
||||
return render.TableData{
|
||||
Namespace: "",
|
||||
Header: render.HeaderRow{
|
||||
render.Header{Name: "a"},
|
||||
render.Header{Name: "b"},
|
||||
render.Header{Name: "c"},
|
||||
},
|
||||
RowEvents: render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
ID: "r1",
|
||||
Fields: render.Fields{"blee", "duh", "fred"},
|
||||
},
|
||||
t := render.NewTableData()
|
||||
t.Namespace = ""
|
||||
t.Header = render.HeaderRow{
|
||||
render.Header{Name: "a"},
|
||||
render.Header{Name: "b"},
|
||||
render.Header{Name: "c"},
|
||||
}
|
||||
t.RowEvents = render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
ID: "r1",
|
||||
Fields: render.Fields{"blee", "duh", "fred"},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
ID: "r2",
|
||||
Fields: render.Fields{"blee", "duh", "zorg"},
|
||||
},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
ID: "r2",
|
||||
Fields: render.Fields{"blee", "duh", "zorg"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return *t
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,9 @@ func (a *App) Resume() {
|
|||
var ctx context.Context
|
||||
ctx, a.cancelFn = context.WithCancel(context.Background())
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
|
|
@ -52,7 +54,7 @@ func (d *Deploy) showPods(app *App, _, _, path string) {
|
|||
app.Flash().Err(err)
|
||||
}
|
||||
|
||||
showPodsFromSelector(app, path, dp.Spec.Selector)
|
||||
showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), dp.Spec.Selector)
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
|
|
@ -47,5 +49,5 @@ func (d *DaemonSet) showPods(app *App, _, _, path string) {
|
|||
d.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
showPodsFromSelector(app, path, ds.Spec.Selector)
|
||||
showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), ds.Spec.Selector)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ type Log struct {
|
|||
cancelFn context.CancelFunc
|
||||
previous bool
|
||||
gvr client.GVR
|
||||
fullScreen bool
|
||||
}
|
||||
|
||||
var _ model.Component = &Log{}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func (l *LogsExtender) logsCmd(prev bool) func(evt *tcell.EventKey) *tcell.Event
|
|||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
if l.GetTable().Path != "" {
|
||||
if isResourcePath(l.GetTable().Path) {
|
||||
path = l.GetTable().Path
|
||||
}
|
||||
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) {
|
||||
log.Debug().Msgf("SHOWING LOGS path %q", path)
|
||||
co := ""
|
||||
if l.containerFn != nil {
|
||||
log.Debug().Msgf("CUSTOM CO FUNC")
|
||||
co = l.containerFn()
|
||||
}
|
||||
if err := l.App().inject(NewLog(client.GVR(l.GVR()), path, co, prev)); err != nil {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
|
|
@ -51,5 +53,5 @@ func (s *StatefulSet) showPods(app *App, _, gvr, path string) {
|
|||
app.Flash().Err(err)
|
||||
}
|
||||
|
||||
showPodsFromSelector(app, path, sts.Spec.Selector)
|
||||
showPodsFromSelector(app, strings.Replace(path, "/", "::", 1), sts.Spec.Selector)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,28 +33,28 @@ func TestTableNew(t *testing.T) {
|
|||
v := NewTable("test")
|
||||
v.Init(makeContext())
|
||||
|
||||
data := render.TableData{
|
||||
Header: render.HeaderRow{
|
||||
render.Header{Name: "NAMESPACE"},
|
||||
render.Header{Name: "NAME", Align: tview.AlignRight},
|
||||
render.Header{Name: "FRED"},
|
||||
render.Header{Name: "AGE", Decorator: render.AgeDecorator},
|
||||
},
|
||||
RowEvents: render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "a", "10", "3m"},
|
||||
},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "b", "15", "1m"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "",
|
||||
data := render.NewTableData()
|
||||
data.Header = render.HeaderRow{
|
||||
render.Header{Name: "NAMESPACE"},
|
||||
render.Header{Name: "NAME", Align: tview.AlignRight},
|
||||
render.Header{Name: "FRED"},
|
||||
render.Header{Name: "AGE", Decorator: render.AgeDecorator},
|
||||
}
|
||||
v.Update(data)
|
||||
data.RowEvents = render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "a", "10", "3m"},
|
||||
},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "b", "15", "1m"},
|
||||
},
|
||||
},
|
||||
}
|
||||
data.Namespace = ""
|
||||
|
||||
v.Update(*data)
|
||||
assert.Equal(t, 3, v.GetRowCount())
|
||||
}
|
||||
|
||||
|
|
@ -101,28 +101,30 @@ func (t *testTableModel) InNamespace(string) bool { return true }
|
|||
func (t *testTableModel) SetRefreshRate(time.Duration) {}
|
||||
|
||||
func makeTableData() render.TableData {
|
||||
return render.TableData{
|
||||
Header: render.HeaderRow{
|
||||
render.Header{Name: "NAMESPACE"},
|
||||
render.Header{Name: "NAME", Align: tview.AlignRight},
|
||||
render.Header{Name: "FRED"},
|
||||
render.Header{Name: "AGE", Decorator: render.AgeDecorator},
|
||||
},
|
||||
RowEvents: render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "blee", "10", "3m"},
|
||||
},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "fred", "15", "1m"},
|
||||
},
|
||||
Deltas: render.DeltaRow{"", "", "20", ""},
|
||||
},
|
||||
},
|
||||
Namespace: "",
|
||||
t := render.NewTableData()
|
||||
|
||||
t.Header = render.HeaderRow{
|
||||
render.Header{Name: "NAMESPACE"},
|
||||
render.Header{Name: "NAME", Align: tview.AlignRight},
|
||||
render.Header{Name: "FRED"},
|
||||
render.Header{Name: "AGE", Decorator: render.AgeDecorator},
|
||||
}
|
||||
t.RowEvents = render.RowEvents{
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "blee", "10", "3m"},
|
||||
},
|
||||
},
|
||||
render.RowEvent{
|
||||
Row: render.Row{
|
||||
Fields: render.Fields{"ns1", "fred", "15", "1m"},
|
||||
},
|
||||
Deltas: render.DeltaRow{"", "", "20", ""},
|
||||
},
|
||||
}
|
||||
t.Namespace = ""
|
||||
|
||||
return *t
|
||||
}
|
||||
|
||||
func makeContext() context.Context {
|
||||
|
|
|
|||
8
main.go
8
main.go
|
|
@ -8,9 +8,6 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -23,11 +20,6 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
http.ListenAndServe("localhost:6060", nil)
|
||||
}()
|
||||
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
|
||||
|
||||
cmd.Execute()
|
||||
|
|
|
|||
Loading…
Reference in New Issue