From a839b7f84a2bc832be715af86c3241542a432427 Mon Sep 17 00:00:00 2001 From: derailed Date: Sun, 5 May 2019 14:50:34 -0600 Subject: [PATCH] fix navigator --- internal/config/ns.go | 2 +- internal/k8s/base.go | 10 +++ internal/resource/base.go | 12 ++++ internal/resource/list.go | 15 +++-- internal/resource/mock_cruder_test.go | 64 +++++++++++++++++++ .../resource/mock_switchablecruder_test.go | 64 +++++++++++++++++++ internal/views/cluster_info.go | 5 +- internal/views/dp.go | 2 +- internal/views/no.go | 2 +- internal/views/pod.go | 6 +- internal/watch/container.go | 5 +- internal/watch/container_test.go | 5 +- internal/watch/meta.go | 13 ++-- internal/watch/meta_test.go | 9 +-- internal/watch/no.go | 5 +- internal/watch/no_mx.go | 4 +- internal/watch/no_mx_test.go | 5 +- internal/watch/no_test.go | 5 +- internal/watch/pod.go | 60 +++++++++++++++-- internal/watch/pod_mx.go | 4 +- internal/watch/pod_mx_test.go | 5 +- internal/watch/pod_test.go | 61 +----------------- 22 files changed, 261 insertions(+), 102 deletions(-) diff --git a/internal/config/ns.go b/internal/config/ns.go index 24cdc397..10e941ce 100644 --- a/internal/config/ns.go +++ b/internal/config/ns.go @@ -48,7 +48,7 @@ func (n *Namespace) Validate(c Connection, ks KubeSettings) { // SetActive set the active namespace. func (n *Namespace) SetActive(ns string, ks KubeSettings) error { - log.Debug().Msgf("Setting active ns %s", ns) + log.Debug().Msgf("Setting active ns %q", ns) n.Active = ns if ns != "" { n.addFavNS(ns) diff --git a/internal/k8s/base.go b/internal/k8s/base.go index cf849dbe..604dbacf 100644 --- a/internal/k8s/base.go +++ b/internal/k8s/base.go @@ -15,6 +15,16 @@ func (b *base) SetLabelSelector(s string) { b.labelSelector = s } +// GetFieldSelector returns field selector. +func (b *base) GetFieldSelector() string { + return b.fieldSelector +} + +// GetLabelSelector returns label selector. +func (b *base) GetLabelSelector() string { + return b.labelSelector +} + func (b *base) HasSelectors() bool { return b.labelSelector != "" || b.fieldSelector != "" } diff --git a/internal/resource/base.go b/internal/resource/base.go index e9ef0056..ffa8aba0 100644 --- a/internal/resource/base.go +++ b/internal/resource/base.go @@ -23,6 +23,8 @@ type ( Delete(ns string, name string) error SetLabelSelector(string) SetFieldSelector(string) + GetLabelSelector() string + GetFieldSelector() string HasSelectors() bool } @@ -70,6 +72,16 @@ func (b *Base) SetLabelSelector(s string) { b.Resource.SetLabelSelector(s) } +// GetFieldSelector returns field selector. +func (b *Base) GetFieldSelector() string { + return b.Resource.GetFieldSelector() +} + +// GetLabelSelector returns label selector. +func (b *Base) GetLabelSelector() string { + return b.Resource.GetLabelSelector() +} + // Name returns the resource namespaced name. func (b *Base) Name() string { return b.path diff --git a/internal/resource/list.go b/internal/resource/list.go index fb7e21c3..a140aa30 100644 --- a/internal/resource/list.go +++ b/internal/resource/list.go @@ -108,6 +108,8 @@ type ( Header(ns string) Row SetFieldSelector(string) SetLabelSelector(string) + GetFieldSelector() string + GetLabelSelector() string HasSelectors() bool } @@ -232,12 +234,16 @@ func metaFQN(m metav1.ObjectMeta) string { } func (l *list) fetchFromStore(m *wa.Meta, ns string) (Columnars, error) { - rr, err := m.List(l.name, ns) + rr, err := m.List(l.name, ns, metav1.ListOptions{ + FieldSelector: l.resource.GetFieldSelector(), + LabelSelector: l.resource.GetLabelSelector(), + }) if err != nil { return nil, err } items := make(Columnars, 0, len(rr)) + opts := metav1.GetOptions{} for _, r := range rr { var ( fqn string @@ -247,7 +253,7 @@ func (l *list) fetchFromStore(m *wa.Meta, ns string) (Columnars, error) { case *v1.Node: fqn = metaFQN(o.ObjectMeta) res = l.resource.New(r) - nmx, err := m.Get(wa.NodeMXIndex, fqn) + nmx, err := m.Get(wa.NodeMXIndex, fqn, opts) if err != nil { log.Warn().Err(err).Msg("NodeMetrics") } @@ -257,7 +263,7 @@ func (l *list) fetchFromStore(m *wa.Meta, ns string) (Columnars, error) { case *v1.Pod: fqn = metaFQN(o.ObjectMeta) res = l.resource.New(r) - pmx, err := m.Get(wa.PodMXIndex, fqn) + pmx, err := m.Get(wa.PodMXIndex, fqn, opts) if err != nil { log.Warn().Err(err).Msg("PodMetrics") } @@ -267,7 +273,7 @@ func (l *list) fetchFromStore(m *wa.Meta, ns string) (Columnars, error) { case v1.Container: fqn = ns res = l.resource.New(r) - pmx, err := m.Get(wa.PodMXIndex, fqn) + pmx, err := m.Get(wa.PodMXIndex, fqn, opts) if err != nil { log.Warn().Err(err).Msg("PodMetrics") } @@ -294,7 +300,6 @@ func (l *list) Reconcile(m *wa.Meta, path *string) error { if rr, err := l.fetchFromStore(m, ns); err == nil { items = rr } else { - log.Debug().Msg("Standard load") items, err = l.resource.List(l.namespace) if err != nil { return err diff --git a/internal/resource/mock_cruder_test.go b/internal/resource/mock_cruder_test.go index 0e2bbb0d..81878095 100644 --- a/internal/resource/mock_cruder_test.go +++ b/internal/resource/mock_cruder_test.go @@ -52,6 +52,36 @@ func (mock *MockCruder) Get(_param0 string, _param1 string) (interface{}, error) return ret0, ret1 } +func (mock *MockCruder) GetFieldSelector() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockCruder().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetFieldSelector", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var ret0 string + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + } + return ret0 +} + +func (mock *MockCruder) GetLabelSelector() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockCruder().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetLabelSelector", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var ret0 string + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + } + return ret0 +} + func (mock *MockCruder) HasSelectors() bool { if mock == nil { panic("mock must not be nil. Use myMock := NewMockCruder().") @@ -201,6 +231,40 @@ func (c *Cruder_Get_OngoingVerification) GetAllCapturedArguments() (_param0 []st return } +func (verifier *VerifierCruder) GetFieldSelector() *Cruder_GetFieldSelector_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetFieldSelector", params, verifier.timeout) + return &Cruder_GetFieldSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type Cruder_GetFieldSelector_OngoingVerification struct { + mock *MockCruder + methodInvocations []pegomock.MethodInvocation +} + +func (c *Cruder_GetFieldSelector_OngoingVerification) GetCapturedArguments() { +} + +func (c *Cruder_GetFieldSelector_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierCruder) GetLabelSelector() *Cruder_GetLabelSelector_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetLabelSelector", params, verifier.timeout) + return &Cruder_GetLabelSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type Cruder_GetLabelSelector_OngoingVerification struct { + mock *MockCruder + methodInvocations []pegomock.MethodInvocation +} + +func (c *Cruder_GetLabelSelector_OngoingVerification) GetCapturedArguments() { +} + +func (c *Cruder_GetLabelSelector_OngoingVerification) GetAllCapturedArguments() { +} + func (verifier *VerifierCruder) HasSelectors() *Cruder_HasSelectors_OngoingVerification { params := []pegomock.Param{} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "HasSelectors", params, verifier.timeout) diff --git a/internal/resource/mock_switchablecruder_test.go b/internal/resource/mock_switchablecruder_test.go index 917c28d1..e43297dc 100644 --- a/internal/resource/mock_switchablecruder_test.go +++ b/internal/resource/mock_switchablecruder_test.go @@ -52,6 +52,36 @@ func (mock *MockSwitchableCruder) Get(_param0 string, _param1 string) (interface return ret0, ret1 } +func (mock *MockSwitchableCruder) GetFieldSelector() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetFieldSelector", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var ret0 string + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + } + return ret0 +} + +func (mock *MockSwitchableCruder) GetLabelSelector() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetLabelSelector", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var ret0 string + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + } + return ret0 +} + func (mock *MockSwitchableCruder) HasSelectors() bool { if mock == nil { panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().") @@ -231,6 +261,40 @@ func (c *SwitchableCruder_Get_OngoingVerification) GetAllCapturedArguments() (_p return } +func (verifier *VerifierSwitchableCruder) GetFieldSelector() *SwitchableCruder_GetFieldSelector_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetFieldSelector", params, verifier.timeout) + return &SwitchableCruder_GetFieldSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type SwitchableCruder_GetFieldSelector_OngoingVerification struct { + mock *MockSwitchableCruder + methodInvocations []pegomock.MethodInvocation +} + +func (c *SwitchableCruder_GetFieldSelector_OngoingVerification) GetCapturedArguments() { +} + +func (c *SwitchableCruder_GetFieldSelector_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierSwitchableCruder) GetLabelSelector() *SwitchableCruder_GetLabelSelector_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetLabelSelector", params, verifier.timeout) + return &SwitchableCruder_GetLabelSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type SwitchableCruder_GetLabelSelector_OngoingVerification struct { + mock *MockSwitchableCruder + methodInvocations []pegomock.MethodInvocation +} + +func (c *SwitchableCruder_GetLabelSelector_OngoingVerification) GetCapturedArguments() { +} + +func (c *SwitchableCruder_GetLabelSelector_OngoingVerification) GetAllCapturedArguments() { +} + func (verifier *VerifierSwitchableCruder) HasSelectors() *SwitchableCruder_HasSelectors_OngoingVerification { params := []pegomock.Param{} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "HasSelectors", params, verifier.timeout) diff --git a/internal/views/cluster_info.go b/internal/views/cluster_info.go index 5366af19..c537b570 100644 --- a/internal/views/cluster_info.go +++ b/internal/views/cluster_info.go @@ -10,6 +10,7 @@ import ( "github.com/derailed/tview" "github.com/gdamore/tcell" "github.com/rs/zerolog/log" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type clusterInfoView struct { @@ -103,12 +104,12 @@ func (v *clusterInfoView) refresh() { v.GetCell(row, 1).SetText(cluster.Version()) row++ - nos, err := v.app.informer.List(watch.NodeIndex, "") + nos, err := v.app.informer.List(watch.NodeIndex, "", metav1.ListOptions{}) if err != nil { log.Warn().Err(err).Msg("List nodes") return } - nmx, err := v.app.informer.List(watch.NodeMXIndex, "") + nmx, err := v.app.informer.List(watch.NodeMXIndex, "", metav1.ListOptions{}) if err != nil { log.Warn().Err(err).Msg("List node metrics") return diff --git a/internal/views/dp.go b/internal/views/dp.go index e5f93af9..095cb825 100644 --- a/internal/views/dp.go +++ b/internal/views/dp.go @@ -60,7 +60,7 @@ func (v *deployView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey { v.app.flash(flashErr, err.Error()) return evt } - showPods(v.app, "", "Deployment", v.selectedItem, sel.String(), "", v.backCmd) + showPods(v.app, ns, "Deployment", v.selectedItem, sel.String(), "", v.backCmd) return nil } diff --git a/internal/views/no.go b/internal/views/no.go index d852e2fb..948cdb2e 100644 --- a/internal/views/no.go +++ b/internal/views/no.go @@ -62,6 +62,6 @@ func showPods(app *appView, ns, res, selected, labelSel, fieldSel string, b acti aa[tcell.KeyEsc] = newKeyAction("Back", b, true) }) // Reset active namespace to all. - app.config.SetActiveNamespace("") + app.config.SetActiveNamespace(ns) app.inject(pv) } diff --git a/internal/views/pod.go b/internal/views/pod.go index 1a0e13ea..df2dd28c 100644 --- a/internal/views/pod.go +++ b/internal/views/pod.go @@ -10,6 +10,7 @@ import ( "github.com/gdamore/tcell" "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const containerFmt = "[fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])" @@ -52,10 +53,7 @@ func (v *podView) listContainers(app *appView, _, res, sel string) { return } - log.Debug().Msgf("Selected %s", sel) - // ns, n := namespaced(sel) - po, err := v.app.informer.Get(watch.PodIndex, sel) - // po, err := app.conn().DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{}) + po, err := v.app.informer.Get(watch.PodIndex, sel, metav1.GetOptions{}) if err != nil { log.Error().Err(err).Msgf("Unable to retrieve pod %s", sel) app.flash(flashErr, err.Error()) diff --git a/internal/watch/container.go b/internal/watch/container.go index 21dc1e9c..44038c3f 100644 --- a/internal/watch/container.go +++ b/internal/watch/container.go @@ -6,6 +6,7 @@ import ( "github.com/derailed/k9s/internal/k8s" "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( @@ -31,7 +32,7 @@ func (c *Container) StartWatching(stopCh <-chan struct{}) {} func (c *Container) Run(closeCh <-chan struct{}) {} // Get retrieves a given container from store. -func (c *Container) Get(fqn string) (interface{}, error) { +func (c *Container) Get(fqn string, opts metav1.GetOptions) (interface{}, error) { o, ok, err := c.GetStore().GetByKey(fqn) if err != nil { return nil, err @@ -47,7 +48,7 @@ func (c *Container) Get(fqn string) (interface{}, error) { } // List retrieves a given containers from store. -func (c *Container) List(fqn string) k8s.Collection { +func (c *Container) List(fqn string, opts metav1.ListOptions) k8s.Collection { o, ok, err := c.GetStore().GetByKey(fqn) if err != nil { log.Error().Err(err).Msg("Pod") diff --git a/internal/watch/container_test.go b/internal/watch/container_test.go index 64e64250..81998d11 100644 --- a/internal/watch/container_test.go +++ b/internal/watch/container_test.go @@ -4,6 +4,7 @@ import ( "testing" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // "github.com/stretchr/testify/assert" ) @@ -12,7 +13,7 @@ func TestContainerGet(t *testing.T) { c := NewContainer(NewPod(cmo, "")) - o, err := c.Get("fred") + o, err := c.Get("fred", metav1.GetOptions{}) assert.ErrorContains(t, err, "not found") assert.Assert(t, o == nil) } @@ -22,7 +23,7 @@ func TestContainerList(t *testing.T) { c := NewContainer(NewPod(cmo, "")) - o := c.List("fred") + o := c.List("fred", metav1.ListOptions{}) assert.Assert(t, o == nil) } diff --git a/internal/watch/meta.go b/internal/watch/meta.go index 1611a15b..98566ef9 100644 --- a/internal/watch/meta.go +++ b/internal/watch/meta.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/derailed/k9s/internal/k8s" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" ) @@ -37,8 +38,8 @@ type TableListenerFn func(TableData) // StoreInformer an informer that allows listeners registration. type StoreInformer interface { cache.SharedIndexInformer - Get(fqn string) (interface{}, error) - List(ns string) k8s.Collection + Get(fqn string, opts metav1.GetOptions) (interface{}, error) + List(ns string, opts metav1.ListOptions) k8s.Collection } // Meta represents a collection of cluster wide watchers. @@ -72,21 +73,21 @@ func (m *Meta) init(ns string) { } // List items from store. -func (m *Meta) List(res, ns string) (k8s.Collection, error) { +func (m *Meta) List(res, ns string, opts metav1.ListOptions) (k8s.Collection, error) { if m == nil { return nil, fmt.Errorf("No meta exists") } if i, ok := m.informers[res]; ok { - return i.List(ns), nil + return i.List(ns, opts), nil } return nil, fmt.Errorf("No informer found for resource %s", res) } // Get a resource by name. -func (m Meta) Get(res, fqn string) (interface{}, error) { +func (m Meta) Get(res, fqn string, opts metav1.GetOptions) (interface{}, error) { if informer, ok := m.informers[res]; ok { - return informer.Get(fqn) + return informer.Get(fqn, opts) } return nil, fmt.Errorf("No informer found for resource %s", res) diff --git a/internal/watch/meta_test.go b/internal/watch/meta_test.go index 766747eb..d0eb5ce0 100644 --- a/internal/watch/meta_test.go +++ b/internal/watch/meta_test.go @@ -4,13 +4,14 @@ import ( "testing" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestMetaList(t *testing.T) { cmo := NewMockConnection() m := NewMeta(cmo, "") - o, err := m.List(PodIndex, "fred") + o, err := m.List(PodIndex, "fred", metav1.ListOptions{}) assert.NilError(t, err) assert.Assert(t, len(o) == 0) } @@ -19,7 +20,7 @@ func TestMetaListNoRes(t *testing.T) { cmo := NewMockConnection() m := NewMeta(cmo, "") - o, err := m.List("dp", "fred") + o, err := m.List("dp", "fred", metav1.ListOptions{}) assert.ErrorContains(t, err, "No informer found") assert.Assert(t, len(o) == 0) } @@ -28,7 +29,7 @@ func TestMetaGet(t *testing.T) { cmo := NewMockConnection() m := NewMeta(cmo, "") - o, err := m.Get(PodIndex, "fred") + o, err := m.Get(PodIndex, "fred", metav1.GetOptions{}) assert.ErrorContains(t, err, "Pod fred not found") assert.Assert(t, o == nil) } @@ -37,7 +38,7 @@ func TestMetaGetNoRes(t *testing.T) { cmo := NewMockConnection() m := NewMeta(cmo, "") - o, err := m.Get("rs", "fred") + o, err := m.Get("rs", "fred", metav1.GetOptions{}) assert.ErrorContains(t, err, "No informer found") assert.Assert(t, o == nil) } diff --git a/internal/watch/no.go b/internal/watch/no.go index 0a6673ba..8c0bb153 100644 --- a/internal/watch/no.go +++ b/internal/watch/no.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/derailed/k9s/internal/k8s" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" wv1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/tools/cache" ) @@ -27,7 +28,7 @@ func NewNode(c k8s.Connection) *Node { } // List all nodes. -func (n *Node) List(_ string) k8s.Collection { +func (n *Node) List(_ string, opts metav1.ListOptions) k8s.Collection { var res k8s.Collection for _, o := range n.GetStore().List() { res = append(res, o) @@ -37,7 +38,7 @@ func (n *Node) List(_ string) k8s.Collection { } // Get retrieves a given node from store. -func (n *Node) Get(fqn string) (interface{}, error) { +func (n *Node) Get(fqn string, opts metav1.GetOptions) (interface{}, error) { o, ok, err := n.GetStore().GetByKey(fqn) if err != nil { return nil, err diff --git a/internal/watch/no_mx.go b/internal/watch/no_mx.go index 1d267b8e..8e1d55d1 100644 --- a/internal/watch/no_mx.go +++ b/internal/watch/no_mx.go @@ -36,12 +36,12 @@ func NewNodeMetrics(c k8s.Connection) *NodeMetrics { } // List node metrics from store. -func (p *NodeMetrics) List(string) k8s.Collection { +func (p *NodeMetrics) List(_ string, opts metav1.ListOptions) k8s.Collection { return p.GetStore().List() } // Get node metrics from store. -func (p *NodeMetrics) Get(MetaFQN string) (interface{}, error) { +func (p *NodeMetrics) Get(MetaFQN string, opts metav1.GetOptions) (interface{}, error) { o, ok, err := p.GetStore().GetByKey(MetaFQN) if err != nil { return nil, err diff --git a/internal/watch/no_mx_test.go b/internal/watch/no_mx_test.go index 8365a5f0..a14851da 100644 --- a/internal/watch/no_mx_test.go +++ b/internal/watch/no_mx_test.go @@ -4,13 +4,14 @@ import ( "testing" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestNodeMXList(t *testing.T) { cmo := NewMockConnection() no := NewNodeMetrics(cmo) - o := no.List("") + o := no.List("", metav1.ListOptions{}) assert.Assert(t, len(o) == 0) } @@ -18,7 +19,7 @@ func TestNodeMXGet(t *testing.T) { cmo := NewMockConnection() no := NewNodeMetrics(cmo) - o, err := no.Get("") + o, err := no.Get("", metav1.GetOptions{}) assert.ErrorContains(t, err, "No node metrics") assert.Assert(t, o == nil) } diff --git a/internal/watch/no_test.go b/internal/watch/no_test.go index 2397d51b..c8cf25d5 100644 --- a/internal/watch/no_test.go +++ b/internal/watch/no_test.go @@ -4,13 +4,14 @@ import ( "testing" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestNodeList(t *testing.T) { cmo := NewMockConnection() no := NewNode(cmo) - o := no.List("") + o := no.List("", metav1.ListOptions{}) assert.Assert(t, o == nil) } @@ -18,7 +19,7 @@ func TestNodeGet(t *testing.T) { cmo := NewMockConnection() no := NewNode(cmo) - o, err := no.Get("") + o, err := no.Get("", metav1.GetOptions{}) assert.ErrorContains(t, err, "not found") assert.Assert(t, o == nil) } diff --git a/internal/watch/pod.go b/internal/watch/pod.go index bfacecd9..9a507ea3 100644 --- a/internal/watch/pod.go +++ b/internal/watch/pod.go @@ -2,9 +2,12 @@ package watch import ( "fmt" + "strings" "github.com/derailed/k9s/internal/k8s" + "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" wv1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/tools/cache" ) @@ -30,20 +33,48 @@ func NewPod(c Connection, ns string) *Pod { } } +func toSelector(s string) map[string]string { + var m map[string]string + ls, err := metav1.ParseToLabelSelector(s) + if err != nil { + log.Error().Err(err).Msg("StringToSel") + return m + } + mSel, err := metav1.LabelSelectorAsMap(ls) + if err != nil { + log.Error().Err(err).Msg("SelToMap") + return m + } + + return mSel +} + // List all pods from store in the given namespace. -func (p *Pod) List(ns string) k8s.Collection { +func (p *Pod) List(ns string, opts metav1.ListOptions) k8s.Collection { var res k8s.Collection + var nodeSelector bool + if strings.Contains(opts.FieldSelector, "spec.nodeName") { + nodeSelector = true + } for _, o := range p.GetStore().List() { pod := o.(*v1.Pod) - if ns == "" || pod.Namespace == ns { - res = append(res, pod) + if ns != "" && pod.Namespace != ns { + continue } + if nodeSelector { + if !matchesNode(pod.Spec.NodeName, toSelector(opts.FieldSelector)) { + continue + } + } else if !matchesLabels(pod.ObjectMeta.Labels, toSelector(opts.LabelSelector)) { + continue + } + res = append(res, pod) } return res } // Get retrieves a given pod from store. -func (p *Pod) Get(fqn string) (interface{}, error) { +func (p *Pod) Get(fqn string, opts metav1.GetOptions) (interface{}, error) { o, ok, err := p.GetStore().GetByKey(fqn) if err != nil { return nil, err @@ -54,3 +85,24 @@ func (p *Pod) Get(fqn string) (interface{}, error) { return o, nil } + +func matchesLabels(labels, selector map[string]string) bool { + if len(selector) == 0 { + return true + } + for k, v := range selector { + la, ok := labels[k] + if !ok || la != v { + return false + } + } + + return true +} + +func matchesNode(name string, selector map[string]string) bool { + if len(selector) == 0 { + return true + } + return selector["spec.nodeName"] == name +} diff --git a/internal/watch/pod_mx.go b/internal/watch/pod_mx.go index 72bcb438..63a47979 100644 --- a/internal/watch/pod_mx.go +++ b/internal/watch/pod_mx.go @@ -39,7 +39,7 @@ func NewPodMetrics(c k8s.Connection, ns string) *PodMetrics { } // List pod metrics from store. -func (p *PodMetrics) List(ns string) k8s.Collection { +func (p *PodMetrics) List(ns string, opts metav1.ListOptions) k8s.Collection { var res k8s.Collection for _, o := range p.GetStore().List() { mx := o.(*mv1beta1.PodMetrics) @@ -52,7 +52,7 @@ func (p *PodMetrics) List(ns string) k8s.Collection { } // Get pod metrics from store. -func (p *PodMetrics) Get(fqn string) (interface{}, error) { +func (p *PodMetrics) Get(fqn string, opts metav1.GetOptions) (interface{}, error) { o, ok, err := p.GetStore().GetByKey(fqn) if err != nil { return nil, err diff --git a/internal/watch/pod_mx_test.go b/internal/watch/pod_mx_test.go index 02effdff..a5dd3074 100644 --- a/internal/watch/pod_mx_test.go +++ b/internal/watch/pod_mx_test.go @@ -5,6 +5,7 @@ import ( "github.com/rs/zerolog" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" ) @@ -16,7 +17,7 @@ func TestPodMXList(t *testing.T) { cmo := NewMockConnection() no := NewPodMetrics(cmo, "") - o := no.List("") + o := no.List("", metav1.ListOptions{}) assert.Assert(t, len(o) == 0) } @@ -24,7 +25,7 @@ func TestPodMXGet(t *testing.T) { cmo := NewMockConnection() no := NewPodMetrics(cmo, "") - o, err := no.Get("") + o, err := no.Get("", metav1.GetOptions{}) assert.ErrorContains(t, err, "No pod metrics") assert.Assert(t, o == nil) } diff --git a/internal/watch/pod_test.go b/internal/watch/pod_test.go index e9698bc7..14aec340 100644 --- a/internal/watch/pod_test.go +++ b/internal/watch/pod_test.go @@ -4,13 +4,14 @@ import ( "testing" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestPodList(t *testing.T) { cmo := NewMockConnection() no := NewPod(cmo, "") - o := no.List("") + o := no.List("", metav1.ListOptions{}) assert.Assert(t, o == nil) } @@ -18,63 +19,7 @@ func TestPodGet(t *testing.T) { cmo := NewMockConnection() no := NewPod(cmo, "") - o, err := no.Get("") + o, err := no.Get("", metav1.GetOptions{}) assert.ErrorContains(t, err, "not found") assert.Assert(t, o == nil) } - -// ---------------------------------------------------------------------------- -// Helpers... - -// func makePod() *v1.Pod { -// var i int32 = 1 -// var t = v1.HostPathDirectory -// return &v1.Pod{ -// ObjectMeta: metav1.ObjectMeta{ -// Namespace: "blee", -// Name: "fred", -// Labels: map[string]string{"blee": "duh"}, -// CreationTimestamp: metav1.Time{Time: testTime()}, -// }, -// Spec: v1.PodSpec{ -// Priority: &i, -// PriorityClassName: "bozo", -// Containers: []v1.Container{ -// { -// Name: "fred", -// Image: "blee", -// Env: []v1.EnvVar{ -// { -// Name: "fred", -// Value: "1", -// ValueFrom: &v1.EnvVarSource{ -// ConfigMapKeyRef: &v1.ConfigMapKeySelector{Key: "blee"}, -// }, -// }, -// }, -// }, -// }, -// Volumes: []v1.Volume{ -// { -// Name: "fred", -// VolumeSource: v1.VolumeSource{ -// HostPath: &v1.HostPathVolumeSource{ -// Path: "/blee", -// Type: &t, -// }, -// }, -// }, -// }, -// }, -// Status: v1.PodStatus{ -// Phase: "Running", -// ContainerStatuses: []v1.ContainerStatus{ -// { -// Name: "fred", -// State: v1.ContainerState{Running: &v1.ContainerStateRunning{}}, -// RestartCount: 0, -// }, -// }, -// }, -// } -// }