From b358f3631fdae66aff79d0f2f329d02a3f78cf1e Mon Sep 17 00:00:00 2001 From: derailed Date: Mon, 18 Feb 2019 18:09:49 -0700 Subject: [PATCH] refactor command line args + beefup tests --- config/cluster_test.go | 24 -- config/config_test.go | 108 -------- config/context.go | 76 ----- config/helpers_test.go | 54 ---- config/k9s.go | 46 ---- config/k9s_test.go | 24 -- config/mock_clusterinfo_test.go | 151 ---------- config/ns_test.go | 60 ---- config/test_assets/k9s.yml | 28 -- go.mod | 1 - go.sum | 24 +- internal/cmd/info.go | 31 +++ {cmd => internal/cmd}/root.go | 175 +++++++++--- internal/cmd/version.go | 18 ++ {config => internal/config}/cluster.go | 6 +- internal/config/cluster_test.go | 39 +++ {config => internal/config}/config.go | 98 ++++--- internal/config/config_test.go | 231 ++++++++++++++++ {config => internal/config}/helpers.go | 12 +- internal/config/helpers_test.go | 84 ++++++ internal/config/k9s.go | 83 ++++++ internal/config/k9s_test.go | 81 ++++++ internal/config/mock_kubesettings_test.go | 199 ++++++++++++++ {config => internal/config}/ns.go | 30 +- internal/config/ns_test.go | 89 ++++++ internal/config/test_assets/k8s.yml | 19 ++ internal/config/test_assets/k9s.yml | 28 ++ .../config}/test_assets/k9s1.yml | 0 .../config}/test_assets/k9s_old.yml | 0 internal/config/test_assets/k9s_toast.yml | 28 ++ {config => internal/config}/view.go | 4 +- {config => internal/config}/view_test.go | 13 +- {resource => internal}/k8s/api.go | 143 ++++------ internal/k8s/assets/config | 43 +++ internal/k8s/assets/config.1 | 39 +++ internal/k8s/cluster.go | 45 +++ {resource => internal}/k8s/cluster_role.go | 0 {resource => internal}/k8s/cluster_roleb.go | 0 {resource => internal}/k8s/cm.go | 0 internal/k8s/config.go | 260 ++++++++++++++++++ internal/k8s/config_test.go | 221 +++++++++++++++ {resource => internal}/k8s/context.go | 45 +-- {resource => internal}/k8s/crd.go | 0 {resource => internal}/k8s/cronjob.go | 0 {resource => internal}/k8s/dp.go | 0 {resource => internal}/k8s/ds.go | 0 {resource => internal}/k8s/ep.go | 0 {resource => internal}/k8s/evt.go | 0 {resource => internal}/k8s/hpa.go | 0 {resource => internal}/k8s/ing.go | 0 {resource => internal}/k8s/job.go | 0 {resource => internal}/k8s/metrics.go | 0 {resource => internal}/k8s/no.go | 0 {resource => internal}/k8s/ns.go | 0 {resource => internal}/k8s/pod.go | 0 {resource => internal}/k8s/pv.go | 0 {resource => internal}/k8s/pvc.go | 0 {resource => internal}/k8s/rc.go | 0 {resource => internal}/k8s/resource.go | 0 {resource => internal}/k8s/role.go | 0 {resource => internal}/k8s/role_binding.go | 0 {resource => internal}/k8s/rs.go | 0 {resource => internal}/k8s/sa.go | 0 {resource => internal}/k8s/secret.go | 0 {resource => internal}/k8s/sts.go | 0 {resource => internal}/k8s/svc.go | 0 {resource => internal/resource}/base.go | 2 +- {resource => internal/resource}/cluster.go | 20 +- .../resource}/cluster_test.go | 9 +- {resource => internal/resource}/cm.go | 4 +- {resource => internal/resource}/cm_test.go | 4 +- {resource => internal/resource}/context.go | 4 +- .../resource}/context_test.go | 4 +- {resource => internal/resource}/cr.go | 2 +- {resource => internal/resource}/cr_binding.go | 2 +- .../resource}/cr_binding_test.go | 4 +- {resource => internal/resource}/cr_test.go | 8 +- {resource => internal/resource}/crd.go | 2 +- {resource => internal/resource}/crd_test.go | 4 +- {resource => internal/resource}/cronjob.go | 2 +- .../resource}/cronjob_test.go | 4 +- {resource => internal/resource}/custom.go | 8 +- {resource => internal/resource}/dp.go | 2 +- {resource => internal/resource}/dp_test.go | 4 +- {resource => internal/resource}/ds.go | 2 +- {resource => internal/resource}/ds_test.go | 4 +- {resource => internal/resource}/ep.go | 2 +- {resource => internal/resource}/ep_test.go | 4 +- {resource => internal/resource}/evt.go | 2 +- {resource => internal/resource}/evt_test.go | 4 +- {resource => internal/resource}/helpers.go | 0 .../resource}/helpers_test.go | 0 {resource => internal/resource}/hpa.go | 2 +- {resource => internal/resource}/hpa_test.go | 4 +- {resource => internal/resource}/ing.go | 2 +- {resource => internal/resource}/ing_test.go | 4 +- {resource => internal/resource}/job.go | 2 +- {resource => internal/resource}/job_test.go | 4 +- {resource => internal/resource}/list.go | 2 +- .../resource}/mock_caller_test.go | 4 +- .../resource}/mock_clusterifc_test.go | 66 ++++- .../resource}/mock_metricsifc_test.go | 4 +- .../resource}/mock_resource_test.go | 4 +- .../resource}/mock_switchableres_test.go | 4 +- {resource => internal/resource}/no.go | 2 +- {resource => internal/resource}/no_test.go | 4 +- {resource => internal/resource}/ns.go | 2 +- {resource => internal/resource}/ns_test.go | 4 +- {resource => internal/resource}/pod.go | 2 +- {resource => internal/resource}/pod_test.go | 4 +- {resource => internal/resource}/pv.go | 2 +- {resource => internal/resource}/pv_test.go | 4 +- {resource => internal/resource}/pvc.go | 2 +- {resource => internal/resource}/pvc_test.go | 4 +- {resource => internal/resource}/rc.go | 2 +- {resource => internal/resource}/ro.go | 2 +- {resource => internal/resource}/ro_binding.go | 2 +- .../resource}/ro_binding_int_test.go | 0 .../resource}/ro_binding_test.go | 4 +- {resource => internal/resource}/ro_test.go | 4 +- {resource => internal/resource}/rs.go | 2 +- {resource => internal/resource}/rs_test.go | 4 +- {resource => internal/resource}/sa.go | 2 +- {resource => internal/resource}/sa_test.go | 4 +- {resource => internal/resource}/secret.go | 2 +- .../resource}/secret_test.go | 4 +- {resource => internal/resource}/sts.go | 2 +- {resource => internal/resource}/sts_test.go | 4 +- {resource => internal/resource}/svc.go | 2 +- .../resource}/svc_int_test.go | 0 {resource => internal/resource}/svc_test.go | 4 +- {views => internal/views}/app.go | 48 +--- {views => internal/views}/cmd.go | 0 {views => internal/views}/cmd_buff.go | 0 {views => internal/views}/cmd_buff_test.go | 0 {views => internal/views}/colorer.go | 2 +- {views => internal/views}/colorer_test.go | 2 +- {views => internal/views}/command.go | 9 +- {views => internal/views}/context.go | 9 +- {views => internal/views}/details.go | 0 {views => internal/views}/exec.go | 0 {views => internal/views}/flash.go | 0 {views => internal/views}/flash_test.go | 0 {views => internal/views}/help.go | 2 +- {views => internal/views}/helpers.go | 0 {views => internal/views}/helpers_test.go | 2 +- {views => internal/views}/info.go | 24 +- {views => internal/views}/log.go | 0 {views => internal/views}/logs.go | 4 +- {views => internal/views}/menu.go | 4 +- {views => internal/views}/namespace.go | 7 +- {views => internal/views}/pod.go | 2 +- {views => internal/views}/registrar.go | 10 +- {views => internal/views}/resource.go | 8 +- {views => internal/views}/select_list.go | 2 - {views => internal/views}/splash.go | 0 {views => internal/views}/table.go | 2 +- {views => internal/views}/utils.go | 0 {views => internal/views}/utils_test.go | 0 {views => internal/views}/xray.go | 2 +- {views => internal/views}/yaml.go | 2 +- main.go | 8 +- resource/k8s/cluster.go | 23 -- resource/k8s/cluster_info.go | 15 - 164 files changed, 2105 insertions(+), 1025 deletions(-) delete mode 100644 config/cluster_test.go delete mode 100644 config/config_test.go delete mode 100644 config/context.go delete mode 100644 config/helpers_test.go delete mode 100644 config/k9s.go delete mode 100644 config/k9s_test.go delete mode 100644 config/mock_clusterinfo_test.go delete mode 100644 config/ns_test.go delete mode 100644 config/test_assets/k9s.yml create mode 100644 internal/cmd/info.go rename {cmd => internal/cmd}/root.go (53%) create mode 100644 internal/cmd/version.go rename {config => internal/config}/cluster.go (82%) create mode 100644 internal/config/cluster_test.go rename {config => internal/config}/config.go (54%) create mode 100644 internal/config/config_test.go rename {config => internal/config}/helpers.go (73%) create mode 100644 internal/config/helpers_test.go create mode 100644 internal/config/k9s.go create mode 100644 internal/config/k9s_test.go create mode 100644 internal/config/mock_kubesettings_test.go rename {config => internal/config}/ns.go (70%) create mode 100644 internal/config/ns_test.go create mode 100644 internal/config/test_assets/k8s.yml create mode 100644 internal/config/test_assets/k9s.yml rename {config => internal/config}/test_assets/k9s1.yml (100%) rename {config => internal/config}/test_assets/k9s_old.yml (100%) create mode 100644 internal/config/test_assets/k9s_toast.yml rename {config => internal/config}/view.go (70%) rename {config => internal/config}/view_test.go (56%) rename {resource => internal}/k8s/api.go (63%) create mode 100644 internal/k8s/assets/config create mode 100644 internal/k8s/assets/config.1 create mode 100644 internal/k8s/cluster.go rename {resource => internal}/k8s/cluster_role.go (100%) rename {resource => internal}/k8s/cluster_roleb.go (100%) rename {resource => internal}/k8s/cm.go (100%) create mode 100644 internal/k8s/config.go create mode 100644 internal/k8s/config_test.go rename {resource => internal}/k8s/context.go (58%) rename {resource => internal}/k8s/crd.go (100%) rename {resource => internal}/k8s/cronjob.go (100%) rename {resource => internal}/k8s/dp.go (100%) rename {resource => internal}/k8s/ds.go (100%) rename {resource => internal}/k8s/ep.go (100%) rename {resource => internal}/k8s/evt.go (100%) rename {resource => internal}/k8s/hpa.go (100%) rename {resource => internal}/k8s/ing.go (100%) rename {resource => internal}/k8s/job.go (100%) rename {resource => internal}/k8s/metrics.go (100%) rename {resource => internal}/k8s/no.go (100%) rename {resource => internal}/k8s/ns.go (100%) rename {resource => internal}/k8s/pod.go (100%) rename {resource => internal}/k8s/pv.go (100%) rename {resource => internal}/k8s/pvc.go (100%) rename {resource => internal}/k8s/rc.go (100%) rename {resource => internal}/k8s/resource.go (100%) rename {resource => internal}/k8s/role.go (100%) rename {resource => internal}/k8s/role_binding.go (100%) rename {resource => internal}/k8s/rs.go (100%) rename {resource => internal}/k8s/sa.go (100%) rename {resource => internal}/k8s/secret.go (100%) rename {resource => internal}/k8s/sts.go (100%) rename {resource => internal}/k8s/svc.go (100%) rename {resource => internal/resource}/base.go (97%) rename {resource => internal/resource}/cluster.go (73%) rename {resource => internal/resource}/cluster_test.go (88%) rename {resource => internal/resource}/cm.go (97%) rename {resource => internal/resource}/cm_test.go (98%) rename {resource => internal/resource}/context.go (96%) rename {resource => internal/resource}/context_test.go (97%) rename {resource => internal/resource}/cr.go (98%) rename {resource => internal/resource}/cr_binding.go (98%) rename {resource => internal/resource}/cr_binding_test.go (96%) rename {resource => internal/resource}/cr_test.go (96%) rename {resource => internal/resource}/crd.go (98%) rename {resource => internal/resource}/crd_test.go (97%) rename {resource => internal/resource}/cronjob.go (98%) rename {resource => internal/resource}/cronjob_test.go (98%) rename {resource => internal/resource}/custom.go (96%) rename {resource => internal/resource}/dp.go (98%) rename {resource => internal/resource}/dp_test.go (98%) rename {resource => internal/resource}/ds.go (98%) rename {resource => internal/resource}/ds_test.go (98%) rename {resource => internal/resource}/ep.go (98%) rename {resource => internal/resource}/ep_test.go (97%) rename {resource => internal/resource}/evt.go (98%) rename {resource => internal/resource}/evt_test.go (97%) rename {resource => internal/resource}/helpers.go (100%) rename {resource => internal/resource}/helpers_test.go (100%) rename {resource => internal/resource}/hpa.go (98%) rename {resource => internal/resource}/hpa_test.go (97%) rename {resource => internal/resource}/ing.go (98%) rename {resource => internal/resource}/ing_test.go (97%) rename {resource => internal/resource}/job.go (98%) rename {resource => internal/resource}/job_test.go (98%) rename {resource => internal/resource}/list.go (99%) rename {resource => internal/resource}/mock_caller_test.go (98%) rename {resource => internal/resource}/mock_clusterifc_test.go (62%) rename {resource => internal/resource}/mock_metricsifc_test.go (97%) rename {resource => internal/resource}/mock_resource_test.go (98%) rename {resource => internal/resource}/mock_switchableres_test.go (98%) rename {resource => internal/resource}/no.go (99%) rename {resource => internal/resource}/no_test.go (97%) rename {resource => internal/resource}/ns.go (98%) rename {resource => internal/resource}/ns_test.go (97%) rename {resource => internal/resource}/pod.go (99%) rename {resource => internal/resource}/pod_test.go (98%) rename {resource => internal/resource}/pv.go (98%) rename {resource => internal/resource}/pv_test.go (97%) rename {resource => internal/resource}/pvc.go (98%) rename {resource => internal/resource}/pvc_test.go (97%) rename {resource => internal/resource}/rc.go (98%) rename {resource => internal/resource}/ro.go (98%) rename {resource => internal/resource}/ro_binding.go (98%) rename {resource => internal/resource}/ro_binding_int_test.go (100%) rename {resource => internal/resource}/ro_binding_test.go (96%) rename {resource => internal/resource}/ro_test.go (96%) rename {resource => internal/resource}/rs.go (98%) rename {resource => internal/resource}/rs_test.go (97%) rename {resource => internal/resource}/sa.go (98%) rename {resource => internal/resource}/sa_test.go (97%) rename {resource => internal/resource}/secret.go (98%) rename {resource => internal/resource}/secret_test.go (98%) rename {resource => internal/resource}/sts.go (98%) rename {resource => internal/resource}/sts_test.go (98%) rename {resource => internal/resource}/svc.go (99%) rename {resource => internal/resource}/svc_int_test.go (100%) rename {resource => internal/resource}/svc_test.go (98%) rename {views => internal/views}/app.go (79%) rename {views => internal/views}/cmd.go (100%) rename {views => internal/views}/cmd_buff.go (100%) rename {views => internal/views}/cmd_buff_test.go (100%) rename {views => internal/views}/colorer.go (98%) rename {views => internal/views}/colorer_test.go (99%) rename {views => internal/views}/command.go (88%) rename {views => internal/views}/context.go (84%) rename {views => internal/views}/details.go (100%) rename {views => internal/views}/exec.go (100%) rename {views => internal/views}/flash.go (100%) rename {views => internal/views}/flash_test.go (100%) rename {views => internal/views}/help.go (97%) rename {views => internal/views}/helpers.go (100%) rename {views => internal/views}/helpers_test.go (94%) rename {views => internal/views}/info.go (72%) rename {views => internal/views}/log.go (100%) rename {views => internal/views}/logs.go (98%) rename {views => internal/views}/menu.go (96%) rename {views => internal/views}/namespace.go (91%) rename {views => internal/views}/pod.go (97%) rename {views => internal/views}/registrar.go (96%) rename {views => internal/views}/resource.go (98%) rename {views => internal/views}/select_list.go (91%) rename {views => internal/views}/splash.go (100%) rename {views => internal/views}/table.go (99%) rename {views => internal/views}/utils.go (100%) rename {views => internal/views}/utils_test.go (100%) rename {views => internal/views}/xray.go (97%) rename {views => internal/views}/yaml.go (98%) delete mode 100644 resource/k8s/cluster.go delete mode 100644 resource/k8s/cluster_info.go diff --git a/config/cluster_test.go b/config/cluster_test.go deleted file mode 100644 index 77bd108d..00000000 --- a/config/cluster_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package config_test - -import ( - "testing" - - m "github.com/petergtz/pegomock" - "github.com/derailed/k9s/config" - "github.com/stretchr/testify/assert" -) - -func TestClusterValidate(t *testing.T) { - setup(t) - - ciMock := NewMockClusterInfo() - m.When(ciMock.AllNamespacesOrDie()).ThenReturn([]string{"ns1", "ns2", "default"}) - - c := config.NewCluster() - c.Validate(ciMock) - - assert.Equal(t, "po", c.View.Active) - assert.Equal(t, "default", c.Namespace.Active) - assert.Equal(t, 2, len(c.Namespace.Favorites)) - assert.Equal(t, []string{"all", "default"}, c.Namespace.Favorites) -} diff --git a/config/config_test.go b/config/config_test.go deleted file mode 100644 index b405d4ae..00000000 --- a/config/config_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package config_test - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "testing" - - m "github.com/petergtz/pegomock" - "github.com/derailed/k9s/config" - "github.com/stretchr/testify/assert" -) - -func TestConfigValidate(t *testing.T) { - setup(t) - - assert.Nil(t, config.Load("test_assets/k9s.yml")) - - ciMock := NewMockClusterInfo() - m.When(ciMock.AllNamespacesOrDie()).ThenReturn([]string{"ns1", "ns2", "default"}) - m.When(ciMock.AllClustersOrDie()).ThenReturn([]string{"c1", "c2"}) - - config.Root.Validate(ciMock) - -} - -func TestConfigLoad(t *testing.T) { - assert.Nil(t, config.Load("test_assets/k9s.yml")) - assert.Equal(t, 2, config.Root.K9s.RefreshRate) - assert.Equal(t, 200, config.Root.K9s.LogBufferSize) - - ctx := config.Root.K9s.Context - assert.Equal(t, "minikube", ctx.Active) - assert.NotNil(t, ctx.Clusters) - - nn := []string{ - "default", - "kube-public", - "istio-system", - "all", - "kube-system", - } - assert.Equal(t, "kube-system", ctx.Clusters["minikube"].Namespace.Active) - assert.Equal(t, nn, ctx.Clusters["minikube"].Namespace.Favorites) - assert.Equal(t, "ctx", ctx.Clusters["minikube"].View.Active) -} - -func TestConfigLoadOldCfg(t *testing.T) { - assert.Nil(t, config.Load("test_assets/k9s_old.yml")) -} - -func TestConfigSaveFile(t *testing.T) { - config.Load("test_assets/k9s.yml") - - config.Root.K9s.RefreshRate = 100 - config.Root.K9s.LogBufferSize = 500 - config.Root.K9s.Context.Active = "fred" - - path := filepath.Join("/tmp", "k9s.yml") - err := config.Root.SaveFile(path) - assert.Nil(t, err) - - raw, err := ioutil.ReadFile(path) - assert.Nil(t, err) - assert.Equal(t, expectedConfig, string(raw)) -} - -// Helpers... - -func setup(t *testing.T) { - m.RegisterMockTestingT(t) - m.RegisterMockFailHandler(func(m string, i ...int) { - fmt.Println("Boom!", m, i) - }) -} - -// ---------------------------------------------------------------------------- -// Test Data... - -var expectedConfig = `k9s: - refreshRate: 100 - logBufferSize: 500 - context: - active: fred - clusters: - fred: - namespace: - active: default - favorites: - - default - - kube-public - - istio-system - - all - - kube-system - view: - active: po - minikube: - namespace: - active: kube-system - favorites: - - default - - kube-public - - istio-system - - all - - kube-system - view: - active: ctx -` diff --git a/config/context.go b/config/context.go deleted file mode 100644 index 3da8dc9e..00000000 --- a/config/context.go +++ /dev/null @@ -1,76 +0,0 @@ -package config - -import ( - log "github.com/sirupsen/logrus" -) - -// Context tracks K9s cluster context configuration. -type Context struct { - Active string `yaml:"active"` - Clusters map[string]*Cluster `yaml:"clusters"` -} - -// NewContext creates a new cluster config context. -func NewContext() *Context { - return &Context{Clusters: make(map[string]*Cluster, 1)} -} - -// SetActiveCluster set the active cluster. -func (c *Context) SetActiveCluster(s string) { - c.Active = s - if _, ok := c.Clusters[c.Active]; ok { - return - } - c.Clusters[c.Active] = NewCluster() - return -} - -// ActiveClusterName returns the currently active cluster name. -func (c *Context) ActiveClusterName() string { - return c.Active -} - -// ActiveCluster returns the currently active cluster configuration. -func (c *Context) ActiveCluster() *Cluster { - if cl, ok := c.Clusters[c.Active]; ok { - return cl - } - c.Clusters[c.Active] = NewCluster() - return c.Clusters[c.Active] -} - -// Validate this configuration -func (c *Context) Validate(ci ClusterInfo) { - if len(c.Active) == 0 { - c.Active = ci.ActiveClusterOrDie() - } - - if c.Clusters == nil { - c.Clusters = make(map[string]*Cluster, 1) - } - - cc := ci.AllClustersOrDie() - if len(cc) == 0 { - log.Panic("Unable to find any live clusters in this configuration") - } - if !InList(cc, c.Active) { - c.Active = cc[0] - c.Clusters[cc[0]] = NewCluster() - } - - if len(c.Clusters) == 0 { - c.Clusters[c.Active] = NewCluster() - } - - for k, cl := range c.Clusters { - if !InList(cc, k) { - delete(c.Clusters, k) - } else { - cl.Validate(ci) - } - } -} - -func (c *Context) activeCluster() *Cluster { - return c.Clusters[c.Active] -} diff --git a/config/helpers_test.go b/config/helpers_test.go deleted file mode 100644 index cfcbc559..00000000 --- a/config/helpers_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package config - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestHelperInList(t *testing.T) { - uu := []struct { - item string - list []string - expected bool - }{ - {"a", []string{}, false}, - {"", []string{}, false}, - {"", []string{""}, true}, - {"a", []string{"a", "b", "c", "d"}, true}, - {"z", []string{"a", "b", "c", "d"}, false}, - } - - for _, u := range uu { - assert.Equal(t, u.expected, InList(u.list, u.item)) - } -} - -func TestHelperInNSList(t *testing.T) { - uu := []struct { - item string - list []interface{} - expected bool - }{ - { - "fred", - []interface{}{ - v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "fred"}}, - }, - true, - }, - { - "blee", - []interface{}{ - v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "fred"}}, - }, - false, - }, - } - - for _, u := range uu { - assert.Equal(t, u.expected, InNSList(u.list, u.item)) - } -} diff --git a/config/k9s.go b/config/k9s.go deleted file mode 100644 index d4ae8efd..00000000 --- a/config/k9s.go +++ /dev/null @@ -1,46 +0,0 @@ -package config - -const ( - refreshRate = 2 - logBufferSize = 200 -) - -// K9s tracks K9s configuration options. -type K9s struct { - RefreshRate int `yaml:"refreshRate"` - LogBufferSize int `yaml:"logBufferSize"` - Context *Context `yaml:"context"` -} - -// NewK9s create a new K9s configuration. -func NewK9s() *K9s { - return &K9s{ - RefreshRate: refreshRate, - LogBufferSize: logBufferSize, - Context: NewContext(), - } -} - -// ActiveCluster return the current Cluster config. -func (k K9s) ActiveCluster() *Cluster { - if k.Context == nil { - k.Context = NewContext() - } - return k.Context.ActiveCluster() -} - -// Validate the configuration -func (k K9s) Validate(ci ClusterInfo) { - if k.RefreshRate <= 0 { - k.RefreshRate = refreshRate - } - - if k.LogBufferSize <= 0 { - k.LogBufferSize = logBufferSize - } - - if k.Context == nil { - k.Context = NewContext() - } - k.Context.Validate(ci) -} diff --git a/config/k9s_test.go b/config/k9s_test.go deleted file mode 100644 index c07f413c..00000000 --- a/config/k9s_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package config_test - -import ( - "testing" - - m "github.com/petergtz/pegomock" - "github.com/derailed/k9s/config" - "github.com/stretchr/testify/assert" -) - -func TestK9sValidate(t *testing.T) { - setup(t) - ci := NewMockClusterInfo() - m.When(ci.AllClustersOrDie()).ThenReturn([]string{"c1", "c2"}) - m.When(ci.ActiveClusterOrDie()).ThenReturn("c1") - - c := config.NewK9s() - c.Validate(ci) - - assert.Equal(t, 2, c.RefreshRate) - assert.Equal(t, 200, c.LogBufferSize) - assert.Equal(t, "c1", c.Context.Active) - assert.Equal(t, 1, len(c.Context.Clusters)) -} diff --git a/config/mock_clusterinfo_test.go b/config/mock_clusterinfo_test.go deleted file mode 100644 index bcd45c5d..00000000 --- a/config/mock_clusterinfo_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/config (interfaces: ClusterInfo) - -package config_test - -import ( - pegomock "github.com/petergtz/pegomock" - "reflect" - "time" -) - -type MockClusterInfo struct { - fail func(message string, callerSkip ...int) -} - -func NewMockClusterInfo() *MockClusterInfo { - return &MockClusterInfo{fail: pegomock.GlobalFailHandler} -} - -func (mock *MockClusterInfo) ActiveClusterOrDie() string { - if mock == nil { - panic("mock must not be nil. Use myMock := NewMockClusterInfo().") - } - params := []pegomock.Param{} - result := pegomock.GetGenericMockFrom(mock).Invoke("ActiveClusterOrDie", 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 *MockClusterInfo) AllClustersOrDie() []string { - if mock == nil { - panic("mock must not be nil. Use myMock := NewMockClusterInfo().") - } - params := []pegomock.Param{} - result := pegomock.GetGenericMockFrom(mock).Invoke("AllClustersOrDie", 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 *MockClusterInfo) AllNamespacesOrDie() []string { - if mock == nil { - panic("mock must not be nil. Use myMock := NewMockClusterInfo().") - } - params := []pegomock.Param{} - result := pegomock.GetGenericMockFrom(mock).Invoke("AllNamespacesOrDie", 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 *MockClusterInfo) VerifyWasCalledOnce() *VerifierClusterInfo { - return &VerifierClusterInfo{ - mock: mock, - invocationCountMatcher: pegomock.Times(1), - } -} - -func (mock *MockClusterInfo) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierClusterInfo { - return &VerifierClusterInfo{ - mock: mock, - invocationCountMatcher: invocationCountMatcher, - } -} - -func (mock *MockClusterInfo) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierClusterInfo { - return &VerifierClusterInfo{ - mock: mock, - invocationCountMatcher: invocationCountMatcher, - inOrderContext: inOrderContext, - } -} - -func (mock *MockClusterInfo) VerifyWasCalledEventually(invocationCountMatcher pegomock.Matcher, timeout time.Duration) *VerifierClusterInfo { - return &VerifierClusterInfo{ - mock: mock, - invocationCountMatcher: invocationCountMatcher, - timeout: timeout, - } -} - -type VerifierClusterInfo struct { - mock *MockClusterInfo - invocationCountMatcher pegomock.Matcher - inOrderContext *pegomock.InOrderContext - timeout time.Duration -} - -func (verifier *VerifierClusterInfo) ActiveClusterOrDie() *ClusterInfo_ActiveClusterOrDie_OngoingVerification { - params := []pegomock.Param{} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ActiveClusterOrDie", params, verifier.timeout) - return &ClusterInfo_ActiveClusterOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} -} - -type ClusterInfo_ActiveClusterOrDie_OngoingVerification struct { - mock *MockClusterInfo - methodInvocations []pegomock.MethodInvocation -} - -func (c *ClusterInfo_ActiveClusterOrDie_OngoingVerification) GetCapturedArguments() { -} - -func (c *ClusterInfo_ActiveClusterOrDie_OngoingVerification) GetAllCapturedArguments() { -} - -func (verifier *VerifierClusterInfo) AllClustersOrDie() *ClusterInfo_AllClustersOrDie_OngoingVerification { - params := []pegomock.Param{} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "AllClustersOrDie", params, verifier.timeout) - return &ClusterInfo_AllClustersOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} -} - -type ClusterInfo_AllClustersOrDie_OngoingVerification struct { - mock *MockClusterInfo - methodInvocations []pegomock.MethodInvocation -} - -func (c *ClusterInfo_AllClustersOrDie_OngoingVerification) GetCapturedArguments() { -} - -func (c *ClusterInfo_AllClustersOrDie_OngoingVerification) GetAllCapturedArguments() { -} - -func (verifier *VerifierClusterInfo) AllNamespacesOrDie() *ClusterInfo_AllNamespacesOrDie_OngoingVerification { - params := []pegomock.Param{} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "AllNamespacesOrDie", params, verifier.timeout) - return &ClusterInfo_AllNamespacesOrDie_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} -} - -type ClusterInfo_AllNamespacesOrDie_OngoingVerification struct { - mock *MockClusterInfo - methodInvocations []pegomock.MethodInvocation -} - -func (c *ClusterInfo_AllNamespacesOrDie_OngoingVerification) GetCapturedArguments() { -} - -func (c *ClusterInfo_AllNamespacesOrDie_OngoingVerification) GetAllCapturedArguments() { -} diff --git a/config/ns_test.go b/config/ns_test.go deleted file mode 100644 index ab272ff5..00000000 --- a/config/ns_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package config_test - -import ( - "testing" - - m "github.com/petergtz/pegomock" - "github.com/derailed/k9s/config" - "github.com/stretchr/testify/assert" -) - -func TestNSValidate(t *testing.T) { - setup(t) - - ns := config.NewNamespace() - - ciMock := NewMockClusterInfo() - m.When(ciMock.AllNamespacesOrDie()).ThenReturn([]string{"ns1", "ns2", "default"}) - - ns.Validate(ciMock) - ciMock.VerifyWasCalledOnce() - assert.Equal(t, "default", ns.Active) - assert.Equal(t, []string{"all", "default"}, ns.Favorites) -} - -func TestNSSetActive(t *testing.T) { - uu := []struct { - ns string - fav []string - }{ - {"all", []string{"all", "default", "kube-system"}}, - {"ns1", []string{"ns1", "all", "default", "kube-system"}}, - {"ns2", []string{"ns2", "ns1", "all", "default", "kube-system"}}, - {"ns3", []string{"ns3", "ns2", "ns1", "all", "default", "kube-system"}}, - {"ns4", []string{"ns4", "ns3", "ns2", "ns1", "all", "default", "kube-system"}}, - } - - ns := config.NewNamespace() - for _, u := range uu { - ns.SetActive(u.ns) - assert.Equal(t, u.ns, ns.Active) - assert.Equal(t, u.fav, ns.Favorites) - } -} - -func TestNSRmFavNS(t *testing.T) { - ns := config.NewNamespace() - uu := []struct { - ns string - fav []string - }{ - {"all", []string{"default", "kube-system"}}, - {"kube-system", []string{"default"}}, - {"blee", []string{"default"}}, - } - - for _, u := range uu { - ns.SetActive(u.ns) - assert.Equal(t, u.ns, ns.Active) - } -} diff --git a/config/test_assets/k9s.yml b/config/test_assets/k9s.yml deleted file mode 100644 index f768e72c..00000000 --- a/config/test_assets/k9s.yml +++ /dev/null @@ -1,28 +0,0 @@ -k9s: - refreshRate: 2 - logBufferSize: 200 - context: - active: minikube - clusters: - minikube: - namespace: - active: kube-system - favorites: - - default - - kube-public - - istio-system - - all - - kube-system - view: - active: ctx - fred: - namespace: - active: default - favorites: - - default - - kube-public - - istio-system - - all - - kube-system - view: - active: po \ No newline at end of file diff --git a/go.mod b/go.mod index 0700ec16..a5648481 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.2.2 k8s.io/api v0.0.0-20190202010724-74b699b93c15 - k8s.io/apiextensions-apiserver v0.0.0-20190205053453-ba46448aaa9f // indirect k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467 k8s.io/cli-runtime v0.0.0-20190207094101-a32b78e5dd0a k8s.io/client-go v10.0.0+incompatible diff --git a/go.sum b/go.sum index 29aaf501..d6f96187 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,7 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/go-autorest v11.4.0+incompatible h1:z3Yr6KYqs0nhSNwqGXEBpWK977hxVqsLv2n9PVYcixY= github.com/Azure/go-autorest v11.4.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8 h1:gUqsFVdUKoRHNg8fkFd8gB5OOEa/g5EwlAHznb4zjbI= github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -17,6 +18,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= @@ -25,6 +27,7 @@ github.com/gdamore/tcell v1.1.0/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -33,6 +36,7 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -40,12 +44,14 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4 h1:wAcVWwS69gs5c6cFkCa/ns/eaL2gC761nF8Ugvd1dGw= github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.6.2 h1:8KyC64BiO8ndiGHY5DlFWWdangUPC9QHPakFRre/Ud0= github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -53,25 +59,30 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/k8sland/tview v0.1.1 h1:732F8kcz5EjUAsFTZJ5BkJx3n34+EwiQRuDaegeS2yU= github.com/k8sland/tview v0.1.1/go.mod h1:PwEtOCvGYNgUA2FQuciQBKB6igksu4GHtq3GY6vOkQo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC15uMxFv5FY/J/8vzyaBiArCOkMdFT9Jsw78iY= github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -84,17 +95,23 @@ github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81/go.mod h1:nuBLWZ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 h1:z6tvbDJ5OLJ48FFmnksv04a78maSTRBUIhkdHYV5Y98= github.com/prometheus/common v0.0.0-20181218105931-67670fe90761/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rivo/tview v0.0.0-20190124120153-84fdb36408f3/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -135,6 +152,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e h1:XEcLGV2fKy3FrsoJVCkX+lMhqc9Suj7J5L/wldA1wu4= golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181220000619-583d854617af h1:iQMS7JKv/0w/iiWf1M49Cg3dmOkBoBZT5KheqPDpaac= @@ -151,12 +169,16 @@ google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= @@ -165,8 +187,6 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.0.0-20190202010724-74b699b93c15 h1:AoUGjnJ3PJMFz+Rkp4lx3X+6mPUnY1MESJhbUSGX+pc= k8s.io/api v0.0.0-20190202010724-74b699b93c15/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apiextensions-apiserver v0.0.0-20190205053453-ba46448aaa9f h1:+XEx9/2g9PNRGh8cX3wbIUhFvtkglP/GvJfIdX1IuBs= -k8s.io/apiextensions-apiserver v0.0.0-20190205053453-ba46448aaa9f/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467 h1:zmz9UYvvXrK/B8EDqFuqreJEaXbIWdzEkNgWrN/Cd3o= k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/cli-runtime v0.0.0-20190207094101-a32b78e5dd0a h1:MrGQxLLZ09Bl5hYYU9VlKnhY60bpPlYd9yXOPnxkdc0= diff --git a/internal/cmd/info.go b/internal/cmd/info.go new file mode 100644 index 00000000..308163ed --- /dev/null +++ b/internal/cmd/info.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + "strings" + + "github.com/derailed/k9s/internal/config" + "github.com/spf13/cobra" +) + +func infoCmd() *cobra.Command { + return &cobra.Command{ + Use: "info", + Short: "Print configuration information", + Long: "Print configuration information", + Run: func(cmd *cobra.Command, args []string) { + const ( + cyan = "\033[1;36m%s\033[0m" + green = "\033[1;32m%s\033[0m" + magenta = "\033[1;35m%s\033[0m" + ) + fmt.Printf(cyan+"\n", strings.Repeat("-", 80)) + fmt.Printf(green+"\n", "🐶 K9s Information") + fmt.Printf(magenta, fmt.Sprintf("%-10s", "LogFile:")) + fmt.Printf("%s\n", config.K9sLogs) + fmt.Printf(magenta, fmt.Sprintf("%-10s", "Config:")) + fmt.Printf("%s\n", config.K9sConfigFile) + fmt.Printf(cyan+"\n", strings.Repeat("-", 80)) + }, + } +} diff --git a/cmd/root.go b/internal/cmd/root.go similarity index 53% rename from cmd/root.go rename to internal/cmd/root.go index 6017fb14..293df3ce 100644 --- a/cmd/root.go +++ b/internal/cmd/root.go @@ -2,17 +2,15 @@ package cmd import ( "fmt" - "strings" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource/k8s" - "github.com/derailed/k9s/views" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/k8s" + "github.com/derailed/k9s/internal/views" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - _ "k8s.io/client-go/plugin/pkg/client/auth" ) @@ -27,46 +25,20 @@ var ( date = "n/a" refreshRate int logLevel string - kubeconfig string k8sFlags *genericclioptions.ConfigFlags rootCmd = &cobra.Command{ Use: "k9s", Short: "A graphical CLI for your Kubernetes cluster management.", - Long: `K9s is a Kubernetes CLI to view and manage your Kubernetes clusters.`, + Long: `K9s is a CLI to view and manage your Kubernetes clusters.`, Run: run, } - versionCmd = &cobra.Command{ - Use: "version", - Short: "Print version info", - Long: "Prints version info", - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Version:%s GitCommit:%s On %s\n", version, commit, date) - }, - } - infoCmd = &cobra.Command{ - Use: "info", - Short: "Print configuration information", - Long: "Print configuration information", - Run: func(cmd *cobra.Command, args []string) { - const ( - cyan = "\033[1;36m%s\033[0m" - green = "\033[1;32m%s\033[0m" - magenta = "\033[1;35m%s\033[0m" - ) - fmt.Printf(cyan+"\n", strings.Repeat("-", 80)) - fmt.Printf(green+"\n", "🐶 K9s Information") - fmt.Printf(magenta, fmt.Sprintf("%-10s", "LogFile:")) - fmt.Printf("%s\n", config.K9sLogs) - fmt.Printf(magenta, fmt.Sprintf("%-10s", "Config:")) - fmt.Printf("%s\n", config.K9sConfigFile) - fmt.Printf(cyan+"\n", strings.Repeat("-", 80)) - }, - } ) +var _ config.KubeSettings = &k8s.Config{} + func init() { - rootCmd.AddCommand(versionCmd, infoCmd) + rootCmd.AddCommand(versionCmd(), infoCmd()) rootCmd.Flags().IntVarP( &refreshRate, @@ -82,12 +54,135 @@ func init() { "Specify a log level (info, warn, debug, error, fatal, panic, trace)", ) - k8sFlags = genericclioptions.NewConfigFlags(false) - k8sFlags.AddFlags(rootCmd.PersistentFlags()) + initK8sFlags() } -func initK8s() { - k8s.InitConnection(k8sFlags) +func initK8sFlags() { + k8sFlags = genericclioptions.NewConfigFlags(false) + rootCmd.Flags().StringVar( + k8sFlags.KubeConfig, + "kubeconfig", + "", + "Path to the kubeconfig file to use for CLI requests", + ) + + rootCmd.Flags().StringVar( + k8sFlags.Timeout, + "request-timeout", + "", + "The length of time to wait before giving up on a single server request", + ) + + rootCmd.Flags().StringVar( + k8sFlags.Context, + "context", + "", + "The name of the kuconfig context to use", + ) + + rootCmd.Flags().StringVar( + k8sFlags.ClusterName, + "cluster", + "", + "The name of the kubeconfig cluster to use", + ) + + rootCmd.Flags().StringVar( + k8sFlags.AuthInfoName, + "user", + "", + "The name of the kubeconfig user to use", + ) + + rootCmd.Flags().BoolVar( + k8sFlags.Insecure, + "insecure-skip-tls-verify", + false, + "If true, the server's caCertFile will not be checked for validity", + ) + + rootCmd.Flags().StringVar( + k8sFlags.CAFile, + "certificate-authority", + "", + "Path to a cert file for the certificate authority", + ) + + rootCmd.Flags().StringVar( + k8sFlags.KeyFile, + "client-key", + "", + "Path to a client key file for TLS", + ) + + rootCmd.Flags().StringVar( + k8sFlags.CertFile, + "client-certificate", + "", + "Path to a client certificate file for TLS", + ) + + rootCmd.Flags().StringVar( + k8sFlags.BearerToken, + "token", + "", + "Bearer token for authentication to the API server", + ) + + rootCmd.Flags().StringVar( + k8sFlags.Namespace, + "namespace", + "n", + "If present, the namespace scope for this CLI request", + ) +} + +func initK9s() { + log.Info("🐶 K9s starting up...") + + // Load K9s config file... + cfg := k8s.NewConfig(k8sFlags) + config.Root = config.NewConfig(cfg) + initK9sConfig() + + // Init K8s connection... + k8s.InitConnectionOrDie(cfg) + log.Info("✅ Kubernetes connectivity") + + config.Root.Save() +} + +func initK9sConfig() { + if err := config.Root.Load(config.K9sConfigFile); err != nil { + log.Warnf("Unable to locate K9s config. Generating new configuration...") + } + config.Root.K9s.RefreshRate = refreshRate + + cfg, err := k8sFlags.ToRawKubeConfigLoader().RawConfig() + if err != nil { + panic("Invalid configuration. Unable to connect to api") + } + ctx := cfg.CurrentContext + if isSet(k8sFlags.Context) { + ctx = *k8sFlags.Context + } + config.Root.K9s.CurrentContext = ctx + + log.Debugf("Active Context `%v`", ctx) + + if isSet(k8sFlags.Namespace) { + config.Root.SetActiveNamespace(*k8sFlags.Namespace) + } + + if c, ok := cfg.Contexts[ctx]; ok { + config.Root.K9s.CurrentCluster = c.Cluster + } else { + panic(fmt.Sprintf("The specified context `%s does not exists in kubeconfig", cfg.CurrentContext)) + } +} + +func isSet(s *string) bool { + return s != nil && len(*s) > 0 } // Execute root command @@ -105,7 +200,7 @@ func run(cmd *cobra.Command, args []string) { log.SetLevel(level) log.SetFormatter(&log.TextFormatter{FullTimestamp: true, ForceColors: true}) - initK8s() + initK9s() initStyles() initKeys() diff --git a/internal/cmd/version.go b/internal/cmd/version.go new file mode 100644 index 00000000..3c7eb4ea --- /dev/null +++ b/internal/cmd/version.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func versionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print version info", + Long: "Prints version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("Version:%s GitCommit:%s On %s\n", version, commit, date) + }, + } +} diff --git a/config/cluster.go b/internal/config/cluster.go similarity index 82% rename from config/cluster.go rename to internal/config/cluster.go index decb140e..a581d9d3 100644 --- a/config/cluster.go +++ b/internal/config/cluster.go @@ -12,14 +12,14 @@ func NewCluster() *Cluster { } // Validate a cluster config. -func (c *Cluster) Validate(ci ClusterInfo) { +func (c *Cluster) Validate(ks KubeSettings) { if c.Namespace == nil { c.Namespace = NewNamespace() } - c.Namespace.Validate(ci) + c.Namespace.Validate(ks) if c.View == nil { c.View = NewView() } - c.View.Validate(ci) + c.View.Validate() } diff --git a/internal/config/cluster_test.go b/internal/config/cluster_test.go new file mode 100644 index 00000000..f7697c58 --- /dev/null +++ b/internal/config/cluster_test.go @@ -0,0 +1,39 @@ +package config_test + +import ( + "testing" + + "github.com/derailed/k9s/internal/config" + m "github.com/petergtz/pegomock" + "github.com/stretchr/testify/assert" +) + +func TestClusterValidate(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2", "default"}, nil) + + c := config.NewCluster() + c.Validate(ksMock) + + assert.Equal(t, "po", c.View.Active) + assert.Equal(t, "default", c.Namespace.Active) + assert.Equal(t, 1, len(c.Namespace.Favorites)) + assert.Equal(t, []string{"default"}, c.Namespace.Favorites) +} + +func TestClusterValidateEmpty(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2", "default"}, nil) + + var c config.Cluster + c.Validate(ksMock) + + assert.Equal(t, "po", c.View.Active) + assert.Equal(t, "default", c.Namespace.Active) + assert.Equal(t, 1, len(c.Namespace.Favorites)) + assert.Equal(t, []string{"default"}, c.Namespace.Favorites) +} diff --git a/config/config.go b/internal/config/config.go similarity index 54% rename from config/config.go rename to internal/config/config.go index fbba6575..6b396b2f 100644 --- a/config/config.go +++ b/internal/config/config.go @@ -1,18 +1,22 @@ package config +// BOZO!! Once yaml is stable implement validation +// go get gopkg.in/validator.v2 + import ( "fmt" "io/ioutil" "os" "path/filepath" + "github.com/derailed/k9s/internal/resource" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) var ( // Root K9s configuration. - Root = NewConfig() + Root *Config // K9sHome represent K9s home directory. K9sHome = filepath.Join(mustK9sHome(), ".k9s") // K9sConfigFile represents K9s config file location. @@ -21,46 +25,47 @@ var ( K9sLogs = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", mustK9sUser())) ) -type ClusterInfo interface { - ActiveClusterOrDie() string - AllClustersOrDie() []string - AllNamespacesOrDie() []string +// KubeSettings exposes kubeconfig context informations. +type KubeSettings interface { + CurrentContextName() (string, error) + CurrentClusterName() (string, error) + ClusterNames() ([]string, error) + NamespaceNames() ([]string, error) } // Config tracks K9s configuration options. type Config struct { - K9s *K9s `yaml:"k9s"` + K9s *K9s `yaml:"k9s"` + settings KubeSettings } // NewConfig creates a new default config. -func NewConfig() *Config { - return &Config{K9s: NewK9s()} +func NewConfig(ks KubeSettings) *Config { + return &Config{K9s: NewK9s(), settings: ks} } -// ActiveClusterName fetch the configuration activeCluster. -func (c *Config) ActiveClusterName() string { - if c.K9s.Context == nil { - c.K9s.Context = NewContext() +// Reset the context to the new current context/cluster. +// if it does not exist. +func (c *Config) Reset() { + c.K9s.CurrentContext, c.K9s.CurrentCluster = "", "" +} + +// CurrentCluster fetch the configuration activeCluster. +func (c *Config) CurrentCluster() *Cluster { + if c, ok := c.K9s.Clusters[c.K9s.CurrentCluster]; ok { + return c } - return c.K9s.Context.Active -} - -// ActiveCluster fetch the configuration activeCluster. -func (c *Config) ActiveCluster() *Cluster { - if c.K9s.Context == nil { - c.K9s.Context = NewContext() - } - return c.K9s.ActiveCluster() -} - -// SetActiveCluster set the active cluster to the a new configuration. -func (c *Config) SetActiveCluster(s string) { - c.K9s.Context.SetActiveCluster(s) + return nil } // ActiveNamespace returns the active namespace in the current cluster. func (c *Config) ActiveNamespace() string { - return c.K9s.ActiveCluster().Namespace.Active + if cl := c.CurrentCluster(); cl != nil { + if cl.Namespace != nil { + return cl.Namespace.Active + } + } + return resource.DefaultNamespace } // FavNamespaces returns fav namespaces in the current cluster. @@ -70,7 +75,9 @@ func (c *Config) FavNamespaces() []string { // SetActiveNamespace set the active namespace in the current cluster. func (c *Config) SetActiveNamespace(ns string) { - c.K9s.ActiveCluster().Namespace.SetActive(ns) + if c.K9s.ActiveCluster() != nil { + c.K9s.ActiveCluster().Namespace.SetActive(ns) + } } // ActiveView returns the active view in the current cluster. @@ -83,14 +90,12 @@ func (c *Config) ActiveView() string { // SetActiveView set the currently cluster active view func (c *Config) SetActiveView(view string) { - if c.K9s.Context == nil { - c.K9s.Context = NewContext() - } + c.Dump("ActiveView") c.K9s.ActiveCluster().View.Active = view } // Load K9s configuration from file -func Load(path string) error { +func (c *Config) Load(path string) error { f, err := ioutil.ReadFile(path) if err != nil { return err @@ -98,23 +103,24 @@ func Load(path string) error { var cfg Config if err := yaml.Unmarshal(f, &cfg); err != nil { - Root = NewConfig() return err } - Root = &cfg + c.K9s = cfg.K9s return nil } // Save configuration to disk. -func (c *Config) Save(ci ClusterInfo) error { - c.Validate(ci) +func (c *Config) Save() error { + log.Debugf("[Config] Saving configuration...") + c.Dump("Saving") + + c.Validate() return c.SaveFile(K9sConfigFile) } // SaveFile K9s configuration to disk. func (c *Config) SaveFile(path string) error { - log.Debugf("[Config] Saving configuration") - ensurePath(path, 0755) + EnsurePath(path, DefaultDirMod) cfg, err := yaml.Marshal(c) if err != nil { log.Errorf("[Config] Unable to save K9s config file: %v", err) @@ -123,14 +129,16 @@ func (c *Config) SaveFile(path string) error { return ioutil.WriteFile(path, cfg, 0644) } -func (c *Config) activeCluster() *Cluster { - return c.K9s.Context.Clusters[c.K9s.Context.Active] +// Validate the configuration. +func (c *Config) Validate() { + c.K9s.Validate(c.settings) } -// Validate the configuration. -func (c *Config) Validate(ci ClusterInfo) { - if c.K9s == nil { - c.K9s = NewK9s() +// Dump debug... +func (c *Config) Dump(msg string) { + log.Debug(msg) + log.Debugf("Current Context: %s\n", c.K9s.CurrentCluster) + for k, cl := range c.K9s.Clusters { + log.Debugf("K9s cluster: %s -- %s\n", k, cl.Namespace) } - c.K9s.Validate(ci) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 00000000..ecf58dfa --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,231 @@ +package config_test + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/derailed/k9s/internal/config" + m "github.com/petergtz/pegomock" + "github.com/stretchr/testify/assert" +) + +func TestConfigValidate(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2", "default"}, nil) + m.When(ksMock.ClusterNames()).ThenReturn([]string{"c1", "c2"}, nil) + + cfg := config.NewConfig(ksMock) + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + cfg.Validate() +} + +func TestConfigLoad(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + + assert.Equal(t, 2, cfg.K9s.RefreshRate) + assert.Equal(t, 200, cfg.K9s.LogBufferSize) + assert.Equal(t, "minikube", cfg.K9s.CurrentContext) + assert.Equal(t, "minikube", cfg.K9s.CurrentCluster) + assert.NotNil(t, cfg.K9s.Clusters) + assert.Equal(t, 2, len(cfg.K9s.Clusters)) + assert.Equal(t, 0, len(cfg.K9s.Aliases)) + + nn := []string{ + "default", + "kube-public", + "istio-system", + "all", + "kube-system", + } + + assert.Equal(t, "kube-system", cfg.K9s.Clusters["minikube"].Namespace.Active) + assert.Equal(t, nn, cfg.K9s.Clusters["minikube"].Namespace.Favorites) + assert.Equal(t, "ctx", cfg.K9s.Clusters["minikube"].View.Active) +} + +func TestConfigCurrentCluster(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + assert.NotNil(t, cfg.CurrentCluster()) + assert.Equal(t, "kube-system", cfg.CurrentCluster().Namespace.Active) + assert.Equal(t, "ctx", cfg.CurrentCluster().View.Active) +} + +func TestConfigActiveNamespace(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + assert.Equal(t, "kube-system", cfg.ActiveNamespace()) +} + +func TestConfigActiveNamespaceBlank(t *testing.T) { + cfg := config.Config{K9s: new(config.K9s)} + assert.Equal(t, "default", cfg.ActiveNamespace()) +} + +func TestConfigSetActiveNamespace(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + cfg.SetActiveNamespace("default") + assert.Equal(t, "default", cfg.ActiveNamespace()) +} + +func TestConfigActiveView(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + assert.Equal(t, "ctx", cfg.ActiveView()) +} + +func TestConfigActiveViewBlank(t *testing.T) { + cfg := config.Config{K9s: new(config.K9s)} + assert.Equal(t, "po", cfg.ActiveView()) +} + +func TestConfigSetActiveView(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + cfg.SetActiveView("po") + assert.Equal(t, "po", cfg.ActiveView()) +} + +func TestConfigFavNamespaces(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + expectedNS := []string{"default", "kube-public", "istio-system", "all", "kube-system"} + assert.Equal(t, expectedNS, cfg.FavNamespaces()) +} + +func TestConfigLoadOldCfg(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + assert.Nil(t, cfg.Load("test_assets/k9s_old.yml")) +} + +func TestConfigLoadCrap(t *testing.T) { + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + assert.NotNil(t, cfg.Load("test_assets/k9s_not_there.yml")) +} + +func TestConfigSaveFile(t *testing.T) { + ksMock := NewMockKubeSettings() + m.When(ksMock.CurrentContextName()).ThenReturn("minikube", nil) + m.When(ksMock.CurrentClusterName()).ThenReturn("minikube", nil) + m.When(ksMock.ClusterNames()).ThenReturn([]string{"minikube", "fred", "blee"}, nil) + + cfg := config.NewConfig(ksMock) + cfg.Load("test_assets/k9s.yml") + cfg.K9s.RefreshRate = 100 + cfg.K9s.LogBufferSize = 500 + cfg.K9s.CurrentContext = "blee" + cfg.K9s.CurrentCluster = "blee" + cfg.Validate() + path := filepath.Join("/tmp", "k9s.yml") + err := cfg.SaveFile(path) + assert.Nil(t, err) + + raw, err := ioutil.ReadFile(path) + assert.Nil(t, err) + assert.Equal(t, expectedConfig, string(raw)) +} + +func TestConfigReset(t *testing.T) { + ksMock := NewMockKubeSettings() + m.When(ksMock.CurrentContextName()).ThenReturn("blee", nil) + m.When(ksMock.CurrentClusterName()).ThenReturn("blee", nil) + m.When(ksMock.ClusterNames()).ThenReturn([]string{"blee"}, nil) + + cfg := config.NewConfig(ksMock) + cfg.Load("test_assets/k9s.yml") + cfg.Reset() + cfg.Validate() + + path := filepath.Join("/tmp", "k9s.yml") + err := cfg.SaveFile(path) + assert.Nil(t, err) + + raw, err := ioutil.ReadFile(path) + assert.Nil(t, err) + assert.Equal(t, resetConfig, string(raw)) +} + +// Helpers... + +func setup(t *testing.T) { + m.RegisterMockTestingT(t) + m.RegisterMockFailHandler(func(m string, i ...int) { + fmt.Println("Boom!", m, i) + }) +} + +// ---------------------------------------------------------------------------- +// Test Data... + +var expectedConfig = `k9s: + refreshRate: 100 + logBufferSize: 500 + currentContext: blee + currentCluster: blee + clusters: + blee: + namespace: + active: default + favorites: + - default + view: + active: po + fred: + namespace: + active: default + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: po + minikube: + namespace: + active: kube-system + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: ctx +` + +var resetConfig = `k9s: + refreshRate: 2 + logBufferSize: 200 + currentContext: blee + currentCluster: blee + clusters: + blee: + namespace: + active: default + favorites: + - default + view: + active: po +` diff --git a/config/helpers.go b/internal/config/helpers.go similarity index 73% rename from config/helpers.go rename to internal/config/helpers.go index f8a43e3b..c123116c 100644 --- a/config/helpers.go +++ b/internal/config/helpers.go @@ -6,7 +6,14 @@ import ( "path/filepath" log "github.com/sirupsen/logrus" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" +) + +const ( + // DefaultDirMod default unix perms for k9s directory. + DefaultDirMod os.FileMode = 0755 + // DefaultFileMod default unix perms for k9s files. + DefaultFileMod os.FileMode = 0644 ) // InList check if string is in a collection of strings. @@ -44,7 +51,8 @@ func mustK9sUser() string { return usr.Username } -func ensurePath(path string, mod os.FileMode) { +// EnsurePath ensures a directory exist from the given path. +func EnsurePath(path string, mod os.FileMode) { dir := filepath.Dir(path) if _, err := os.Stat(dir); os.IsNotExist(err) { if err = os.Mkdir(dir, mod); err != nil { diff --git a/internal/config/helpers_test.go b/internal/config/helpers_test.go new file mode 100644 index 00000000..577b07ed --- /dev/null +++ b/internal/config/helpers_test.go @@ -0,0 +1,84 @@ +package config_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/derailed/k9s/internal/config" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestHelperInList(t *testing.T) { + uu := []struct { + item string + list []string + expected bool + }{ + {"a", []string{}, false}, + {"", []string{}, false}, + {"", []string{""}, true}, + {"a", []string{"a", "b", "c", "d"}, true}, + {"z", []string{"a", "b", "c", "d"}, false}, + } + + for _, u := range uu { + assert.Equal(t, u.expected, config.InList(u.list, u.item)) + } +} + +func TestHelperInNSList(t *testing.T) { + uu := []struct { + item string + list []interface{} + expected bool + }{ + { + "fred", + []interface{}{ + v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "fred"}}, + }, + true, + }, + { + "blee", + []interface{}{ + v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "fred"}}, + }, + false, + }, + } + + for _, u := range uu { + assert.Equal(t, u.expected, config.InNSList(u.list, u.item)) + } +} + +func TestEnsurePathNone(t *testing.T) { + var mod os.FileMode = 0744 + dir := filepath.Join("/tmp", "fred") + assert.Nil(t, os.Remove(dir)) + + path := filepath.Join(dir, "duh.yml") + config.EnsurePath(path, mod) + + p, err := os.Stat(dir) + assert.Nil(t, err) + assert.Equal(t, "drwxr--r--", p.Mode().String()) +} + +func TestEnsurePathNoOpt(t *testing.T) { + var mod os.FileMode = 0744 + dir := filepath.Join("/tmp", "blee") + assert.Nil(t, os.Remove(dir)) + assert.Nil(t, os.Mkdir(dir, mod)) + + path := filepath.Join(dir, "duh.yml") + config.EnsurePath(path, mod) + + p, err := os.Stat(dir) + assert.Nil(t, err) + assert.Equal(t, "drwxr--r--", p.Mode().String()) +} diff --git a/internal/config/k9s.go b/internal/config/k9s.go new file mode 100644 index 00000000..5b343e18 --- /dev/null +++ b/internal/config/k9s.go @@ -0,0 +1,83 @@ +package config + +const ( + defaultRefreshRate = 2 + defaultLogBufferSize = 200 +) + +// K9s tracks K9s configuration options. +type K9s struct { + RefreshRate int `yaml:"refreshRate"` + LogBufferSize int `yaml:"logBufferSize"` + CurrentContext string `yaml:"currentContext"` + CurrentCluster string `yaml:"currentCluster"` + Clusters map[string]*Cluster `yaml:"clusters,omitempty"` + Aliases map[string]string `yaml:"aliases,omitempty"` +} + +// NewK9s create a new K9s configuration. +func NewK9s() *K9s { + return &K9s{ + RefreshRate: defaultRefreshRate, + LogBufferSize: defaultLogBufferSize, + Clusters: map[string]*Cluster{}, + Aliases: map[string]string{}, + } +} + +// ActiveCluster returns the currently active cluster. +func (k *K9s) ActiveCluster() *Cluster { + if k.Clusters == nil { + k.Clusters = map[string]*Cluster{} + } + if len(k.CurrentCluster) == 0 { + return nil + } + + if c, ok := k.Clusters[k.CurrentCluster]; ok { + return c + } + k.Clusters[k.CurrentCluster] = NewCluster() + return k.Clusters[k.CurrentCluster] +} + +// Validate the current configuration. +func (k *K9s) Validate(ks KubeSettings) { + if k.RefreshRate <= 0 { + k.RefreshRate = defaultRefreshRate + } + + if k.LogBufferSize <= 0 { + k.LogBufferSize = defaultLogBufferSize + } + + if k.Clusters == nil { + k.Clusters = map[string]*Cluster{} + } + + cc, err := ks.ClusterNames() + if err != nil { + return + } + for key := range k.Clusters { + if !InList(cc, key) { + if k.CurrentCluster == key { + k.CurrentCluster = "" + } + delete(k.Clusters, key) + } + } + + if ctx, err := ks.CurrentContextName(); err == nil && len(k.CurrentContext) == 0 { + k.CurrentContext = ctx + k.CurrentCluster = "" + } + + if cl, err := ks.CurrentClusterName(); err == nil && len(k.CurrentCluster) == 0 { + k.CurrentCluster = cl + } + + if _, ok := k.Clusters[k.CurrentCluster]; !ok { + k.Clusters[k.CurrentCluster] = NewCluster() + } +} diff --git a/internal/config/k9s_test.go b/internal/config/k9s_test.go new file mode 100644 index 00000000..e0a9faf5 --- /dev/null +++ b/internal/config/k9s_test.go @@ -0,0 +1,81 @@ +package config_test + +import ( + "testing" + + "github.com/derailed/k9s/internal/config" + m "github.com/petergtz/pegomock" + "github.com/stretchr/testify/assert" +) + +func TestK9sValidate(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + m.When(ksMock.CurrentContextName()).ThenReturn("ctx1", nil) + m.When(ksMock.CurrentClusterName()).ThenReturn("c1", nil) + m.When(ksMock.ClusterNames()).ThenReturn([]string{"c1", "c2"}, nil) + + c := config.NewK9s() + c.Validate(ksMock) + + assert.Equal(t, 2, c.RefreshRate) + assert.Equal(t, 200, c.LogBufferSize) + assert.Equal(t, "ctx1", c.CurrentContext) + assert.Equal(t, "c1", c.CurrentCluster) + assert.Equal(t, 1, len(c.Clusters)) + _, ok := c.Clusters[c.CurrentCluster] + assert.True(t, ok) +} + +func TestK9sValidateBlank(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + m.When(ksMock.CurrentContextName()).ThenReturn("ctx1", nil) + m.When(ksMock.CurrentClusterName()).ThenReturn("c1", nil) + m.When(ksMock.ClusterNames()).ThenReturn([]string{"c1", "c2"}, nil) + + var c config.K9s + c.Validate(ksMock) + + assert.Equal(t, 2, c.RefreshRate) + assert.Equal(t, 200, c.LogBufferSize) + assert.Equal(t, "ctx1", c.CurrentContext) + assert.Equal(t, "c1", c.CurrentCluster) + assert.Equal(t, 1, len(c.Clusters)) + _, ok := c.Clusters[c.CurrentCluster] + assert.True(t, ok) +} + +func TestK9sActiveClusterZero(t *testing.T) { + setup(t) + + c := config.NewK9s() + c.CurrentCluster = "fred" + cl := c.ActiveCluster() + assert.NotNil(t, cl) + assert.Equal(t, "default", cl.Namespace.Active) + assert.Equal(t, 1, len(cl.Namespace.Favorites)) +} + +func TestK9sActiveClusterBlank(t *testing.T) { + setup(t) + + var c config.K9s + cl := c.ActiveCluster() + assert.Nil(t, cl) +} + +func TestK9sActiveCluster(t *testing.T) { + setup(t) + + ksMock := NewMockKubeSettings() + cfg := config.NewConfig(ksMock) + assert.Nil(t, cfg.Load("test_assets/k9s.yml")) + + cl := cfg.K9s.ActiveCluster() + assert.NotNil(t, cl) + assert.Equal(t, "kube-system", cl.Namespace.Active) + assert.Equal(t, 5, len(cl.Namespace.Favorites)) +} diff --git a/internal/config/mock_kubesettings_test.go b/internal/config/mock_kubesettings_test.go new file mode 100644 index 00000000..501af817 --- /dev/null +++ b/internal/config/mock_kubesettings_test.go @@ -0,0 +1,199 @@ +// Code generated by pegomock. DO NOT EDIT. +// Source: github.com/derailed/k9s/internal/config (interfaces: KubeSettings) + +package config_test + +import ( + pegomock "github.com/petergtz/pegomock" + "reflect" + "time" +) + +type MockKubeSettings struct { + fail func(message string, callerSkip ...int) +} + +func NewMockKubeSettings() *MockKubeSettings { + return &MockKubeSettings{fail: pegomock.GlobalFailHandler} +} + +func (mock *MockKubeSettings) ClusterNames() ([]string, error) { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockKubeSettings().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("ClusterNames", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 []string + var ret1 error + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].([]string) + } + if result[1] != nil { + ret1 = result[1].(error) + } + } + return ret0, ret1 +} + +func (mock *MockKubeSettings) CurrentClusterName() (string, error) { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockKubeSettings().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("CurrentClusterName", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 string + var ret1 error + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + if result[1] != nil { + ret1 = result[1].(error) + } + } + return ret0, ret1 +} + +func (mock *MockKubeSettings) CurrentContextName() (string, error) { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockKubeSettings().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("CurrentContextName", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 string + var ret1 error + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].(string) + } + if result[1] != nil { + ret1 = result[1].(error) + } + } + return ret0, ret1 +} + +func (mock *MockKubeSettings) NamespaceNames() ([]string, error) { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockKubeSettings().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("NamespaceNames", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 []string + var ret1 error + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].([]string) + } + if result[1] != nil { + ret1 = result[1].(error) + } + } + return ret0, ret1 +} + +func (mock *MockKubeSettings) VerifyWasCalledOnce() *VerifierKubeSettings { + return &VerifierKubeSettings{ + mock: mock, + invocationCountMatcher: pegomock.Times(1), + } +} + +func (mock *MockKubeSettings) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierKubeSettings { + return &VerifierKubeSettings{ + mock: mock, + invocationCountMatcher: invocationCountMatcher, + } +} + +func (mock *MockKubeSettings) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierKubeSettings { + return &VerifierKubeSettings{ + mock: mock, + invocationCountMatcher: invocationCountMatcher, + inOrderContext: inOrderContext, + } +} + +func (mock *MockKubeSettings) VerifyWasCalledEventually(invocationCountMatcher pegomock.Matcher, timeout time.Duration) *VerifierKubeSettings { + return &VerifierKubeSettings{ + mock: mock, + invocationCountMatcher: invocationCountMatcher, + timeout: timeout, + } +} + +type VerifierKubeSettings struct { + mock *MockKubeSettings + invocationCountMatcher pegomock.Matcher + inOrderContext *pegomock.InOrderContext + timeout time.Duration +} + +func (verifier *VerifierKubeSettings) ClusterNames() *KubeSettings_ClusterNames_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ClusterNames", params, verifier.timeout) + return &KubeSettings_ClusterNames_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type KubeSettings_ClusterNames_OngoingVerification struct { + mock *MockKubeSettings + methodInvocations []pegomock.MethodInvocation +} + +func (c *KubeSettings_ClusterNames_OngoingVerification) GetCapturedArguments() { +} + +func (c *KubeSettings_ClusterNames_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierKubeSettings) CurrentClusterName() *KubeSettings_CurrentClusterName_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CurrentClusterName", params, verifier.timeout) + return &KubeSettings_CurrentClusterName_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type KubeSettings_CurrentClusterName_OngoingVerification struct { + mock *MockKubeSettings + methodInvocations []pegomock.MethodInvocation +} + +func (c *KubeSettings_CurrentClusterName_OngoingVerification) GetCapturedArguments() { +} + +func (c *KubeSettings_CurrentClusterName_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierKubeSettings) CurrentContextName() *KubeSettings_CurrentContextName_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CurrentContextName", params, verifier.timeout) + return &KubeSettings_CurrentContextName_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type KubeSettings_CurrentContextName_OngoingVerification struct { + mock *MockKubeSettings + methodInvocations []pegomock.MethodInvocation +} + +func (c *KubeSettings_CurrentContextName_OngoingVerification) GetCapturedArguments() { +} + +func (c *KubeSettings_CurrentContextName_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierKubeSettings) NamespaceNames() *KubeSettings_NamespaceNames_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "NamespaceNames", params, verifier.timeout) + return &KubeSettings_NamespaceNames_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type KubeSettings_NamespaceNames_OngoingVerification struct { + mock *MockKubeSettings + methodInvocations []pegomock.MethodInvocation +} + +func (c *KubeSettings_NamespaceNames_OngoingVerification) GetCapturedArguments() { +} + +func (c *KubeSettings_NamespaceNames_OngoingVerification) GetAllCapturedArguments() { +} diff --git a/config/ns.go b/internal/config/ns.go similarity index 70% rename from config/ns.go rename to internal/config/ns.go index 7cfe0d75..31572f56 100644 --- a/config/ns.go +++ b/internal/config/ns.go @@ -1,12 +1,15 @@ package config import ( - "github.com/derailed/k9s/resource" log "github.com/sirupsen/logrus" ) -// MaxFavoritesNS number # favorite namespaces to keep in the configuration. -const MaxFavoritesNS = 10 +const ( + // MaxFavoritesNS number # favorite namespaces to keep in the configuration. + MaxFavoritesNS = 10 + defaultNS = "default" + allNS = "all" +) // Namespace tracks active and favorites namespaces. type Namespace struct { @@ -17,22 +20,25 @@ type Namespace struct { // NewNamespace create a new namespace configuration. func NewNamespace() *Namespace { return &Namespace{ - Active: resource.DefaultNamespace, - Favorites: []string{"all", "default", "kube-system"}, + Active: defaultNS, + Favorites: []string{defaultNS}, } } // Validate a namespace is setup correctly -func (n *Namespace) Validate(ci ClusterInfo) { - nn := ci.AllNamespacesOrDie() +func (n *Namespace) Validate(ks KubeSettings) { + nn, err := ks.NamespaceNames() + if err != nil { + return + } if !n.isAllNamespace() && !InList(nn, n.Active) { - log.Debugf("[Config] Validation error active namespace reseting to `default") - n.Active = resource.DefaultNamespace + log.Debugf("[Config] Validation error active namespace resetting to `default") + n.Active = defaultNS } for _, ns := range n.Favorites { - if ns != resource.AllNamespace && !InList(nn, ns) { + if ns != allNS && !InList(nn, ns) { log.Debugf("[Config] Invalid favorite found '%s' - %t", ns, n.isAllNamespace()) n.rmFavNS(ns) } @@ -46,7 +52,7 @@ func (n *Namespace) SetActive(ns string) { } func (n *Namespace) isAllNamespace() bool { - return n.Active == resource.AllNamespace + return n.Active == allNS } func (n *Namespace) addFavNS(ns string) { @@ -77,4 +83,4 @@ func (n *Namespace) rmFavNS(ns string) { } n.Favorites = append(n.Favorites[:victim], n.Favorites[victim+1:]...) -} \ No newline at end of file +} diff --git a/internal/config/ns_test.go b/internal/config/ns_test.go new file mode 100644 index 00000000..234da773 --- /dev/null +++ b/internal/config/ns_test.go @@ -0,0 +1,89 @@ +package config_test + +import ( + "errors" + "testing" + + "github.com/derailed/k9s/internal/config" + m "github.com/petergtz/pegomock" + "github.com/stretchr/testify/assert" +) + +func TestNSValidate(t *testing.T) { + setup(t) + + ns := config.NewNamespace() + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2", "default"}, nil) + + ns.Validate(ksMock) + ksMock.VerifyWasCalledOnce() + assert.Equal(t, "default", ns.Active) + assert.Equal(t, []string{"default"}, ns.Favorites) +} + +func TestNSValidateMissing(t *testing.T) { + setup(t) + + ns := config.NewNamespace() + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2"}, nil) + ns.Validate(ksMock) + + ksMock.VerifyWasCalledOnce() + assert.Equal(t, "default", ns.Active) + assert.Equal(t, []string{}, ns.Favorites) +} + +func TestNSValidateNoNS(t *testing.T) { + setup(t) + + ns := config.NewNamespace() + + ksMock := NewMockKubeSettings() + m.When(ksMock.NamespaceNames()).ThenReturn([]string{"ns1", "ns2"}, errors.New("boom")) + ns.Validate(ksMock) + + ksMock.VerifyWasCalledOnce() + assert.Equal(t, "default", ns.Active) + assert.Equal(t, []string{"default"}, ns.Favorites) +} + +func TestNSSetActive(t *testing.T) { + uu := []struct { + ns string + fav []string + }{ + {"all", []string{"all", "default"}}, + {"ns1", []string{"ns1", "all", "default"}}, + {"ns2", []string{"ns2", "ns1", "all", "default"}}, + {"ns3", []string{"ns3", "ns2", "ns1", "all", "default"}}, + {"ns4", []string{"ns4", "ns3", "ns2", "ns1", "all", "default"}}, + } + + ns := config.NewNamespace() + for _, u := range uu { + ns.SetActive(u.ns) + assert.Equal(t, u.ns, ns.Active) + assert.Equal(t, u.fav, ns.Favorites) + } +} + +func TestNSRmFavNS(t *testing.T) { + ns := config.NewNamespace() + uu := []struct { + ns string + fav []string + }{ + {"all", []string{"default", "kube-system"}}, + {"kube-system", []string{"default"}}, + {"blee", []string{"default"}}, + } + + for _, u := range uu { + ns.SetActive(u.ns) + assert.Equal(t, u.ns, ns.Active) + } +} diff --git a/internal/config/test_assets/k8s.yml b/internal/config/test_assets/k8s.yml new file mode 100644 index 00000000..21085e0e --- /dev/null +++ b/internal/config/test_assets/k8s.yml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Config +preferences: {} +clusters: +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:6443 + name: docker-for-desktop-cluster +contexts: +- context: + cluster: docker-for-desktop-cluster + user: docker-for-desktop + name: docker-for-desktop +current-context: docker-for-desktop +users: +- name: docker-for-desktop + user: + client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5RENDQWR5Z0F3SUJBZ0lJWFNHb3I3ZlJlOHN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T0RBNU1ESXlNREkyTlRaYUZ3MHlNREF5TURreE5qQXhNVGhhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRGxBTWZKVUUvWUIwb0UKWmN5TmE1S0dVMkZpRmNYLys2dFJQUlpETkhZSkE1ZGtaME40UC9kVVdZeWJGRlVYc0E3UU1Sbm1mS280Q25MTQptK28wS2NUd3NRMnY3UzlPejVJYlJCOVZGVnFqNDJmNW9mVFFDcnZNN20wWVovNlRzcjhtSDE0QVYzWkRZaWtsCkF1VjlqRUgvczF5WWppbG0rODVlbm02RUZYYkJMV2czcXZkQ3VxNmlMa2FjWFptWTJYVXVtTWVOTVZnQllrS1UKVi84czJ0VlhyTlhvaU9qZVFMZlIvYmpvYkFZbzlMM0JWZFczYUxjanBwcDYzWmE0YlZITHYyQ2ZXMDcwNjNvbQpYZ0syM1hHWjQwdFFGaElxbUlvZktYZ0lVSWk2YVV3UVI0WFVRd3RMeTk4aHRDazZ2ckl5UUpMWkdKV29WVlU4CitJclVtZFRyQWdNQkFBR2pKekFsTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFRRzlVaFVjUDdJQzdyZmRyK1pxTTBKbGxITzY3MzZoTgpVRkpvNmZSRUdDbjlxclN6SW44K0RZV1N3RnF4ZVRhZlNFK3VJZHFGREQ1ais1bWhEQzBzZUV2WWlNQ09CZFJDCk4xT3RRK1lrQndndnNKU3RxZGdzNTRXdkJwLzFiS09leFNLS1laTzJPaUJLd3NRV1ZXeksrQ0VjOXhRSm1jN2MKZGhlK0tNZVNTeC9LSmR0bHc4VWVSUkVCOU00WjZjRHpLYzQ2cjhBd04zWkxibzdsYzVCNE0wb2lXMUVwR0wwQgpKUXYrT1FDblV4K0d1dVcvTGdNT1JQRVFXaXF1UjFvWXlJQjlRb0wxRXFCTDZHejhTWjVtbTE1ellWSVZXTHh6ClNQLzVyd2VjNDY0Z216RDR4MHJVUHBIaWlRSVJzUjk1WXBIMjNxWkl2QlVwd2dnTjJnd2hTUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBNVFESHlWQlAyQWRLQkdYTWpXdVNobE5oWWhYRi8vdXJVVDBXUXpSMkNRT1haR2RECmVELzNWRm1NbXhSVkY3QU8wREVaNW55cU9BcHl6SnZxTkNuRThMRU5yKzB2VHMrU0cwUWZWUlZhbytObithSDAKMEFxN3pPNXRHR2YrazdLL0poOWVBRmQyUTJJcEpRTGxmWXhCLzdOY21JNHBadnZPWHA1dWhCVjJ3UzFvTjZyMwpRcnF1b2k1R25GMlptTmwxTHBqSGpURllBV0pDbEZmL0xOclZWNnpWNklqbzNrQzMwZjI0Nkd3R0tQUzl3VlhWCnQyaTNJNmFhZXQyV3VHMVJ5NzlnbjF0TzlPdDZKbDRDdHQxeG1lTkxVQllTS3BpS0h5bDRDRkNJdW1sTUVFZUYKMUVNTFM4dmZJYlFwT3I2eU1rQ1MyUmlWcUZWVlBQaUsxSm5VNndJREFRQUJBb0lCQUZSY29EenlZQ2VXTDlkRQo1VUVuNHRlbk9kWFhiWlNxMHViZm1TYnkyWlRpaE5BUkZwTGpCYXRHUGYwWFZXMmZoeVY5SVN4K3VucGdwdi9uClpEVUpPaXJ0SHJ5enBOemtyTTlzbmhwSy9wUW5mek5BVFo2aWhhS3VKdlI1d3hnSUhsRGQ5MVFxNUQ5WWx3MnkKYm5aOHlBZDV2Ti9hWnpnd0JVdG9GQkNHazdQLzRxK0JlbHZoNWd1SzdzS0dvRi94dGMwWlp6RGtYMkw5VHJlZQowSE5nTmJlYm91SHhlVHBkcGNLQzZ2TENST2tqb0RTdDY1ZEo2ajBGTzhzTERVcWRrWkxNa0ducTdoZWhYV2JwClBtRVI5dWc3Qk1HVUFtcHhpbGdGVHM0MnRSNnoxZXdvSWs5bC92V3ZMTFpZbEE3OUo4YkU0UTNPZGpXc2Nza1YKV0ZpakZ0a0NnWUVBNXU2SnV3b3A0Z2QzTjNhUHVhakpHNEpjYTZNWFZQNEVVay9vY2pobloyRDF5cjd2ZXhZSApVaE1WN2p6dzJUQ1FJa2JtMWZlSTZpa1llUzNVNXprZDhKSm9VaXV4T3ZsS0VJSlFrTEROQyttMHFuN0xEamU1Cmx0SkE3Sm9IdDhSTzNMS1JBTFhvQTVsV0l0NWNQLzVuTW1IMTlDZGQ3L294MU0xRXFFYUxNMmNDZ1lFQS9keWsKMExyR0VtbTg0SlU0dDIvZDVzNm5ERnAxOWxhUXJlbFY1bWRsZEFKNGRPVHkxZXV4dHNFeS8xSFExWXBLa25aSgpTa2Q2RTJzYk1MUncxdzFGWTZpczI0ektmaWtLV0N5SXBPMTkvQWh5UkpweGxKTlN4a2hjK1FpVXVSd1lsVmtMCi9XQ3dFUFVVaDI1VHJRNE9LRTNKazh3VmVmdFlwdkZNRDhvaHc5MENnWUF5ZTkxQ05XT1lsUmM3MmNCcnp2ay8KK1V5by96dGZpalI1cGh4anMrN3ZDNlJRRVZPYkxlS2x6NlJRczZQWFp5VnJTT0szemVoeGdGQm9WVnVndkx6TgoxY1BXaXRTdzFzU1pQVlBOZmNrbG5JNnhZd3lTN0IyM1dmbDFmK3JHQXJWV3kvYWxHQjlEZ2lieGNuanFTSHhZCjZFOXpjNU8yblpSOU4rNlZkdTZCYXdLQmdDV3Vtc2hnOFFYS3JENnA1OEZTMloxcEQyTEdDcnlHSFBPenJ3eUUKVElycjB2V0hCb1M2ZDZhcEJ1amZQQ0IyWnB0Vzg0b1RFZ3ZQMmpsZ2oxOWNtUEF5R1haOWI1RktoajZRWGJnZAppSlhncXhXRDExZzJoaExvcXVSTVljY1laSTNHcWdEeVdUQXJNT0RwZjRJd2srbG5vb1JOeHVKVWJOUmEvTzliCkVhZ0JBb0dBUE9VZk15M3JPSWRBRTV4Q0VxOUlza1hXblFrcmRwYktKVDVzbVFyVUhuRFh4QWM5L0libm1jWmwKOWN4Y3czdktMbWVZU3loWFF5ZWF1VXo3amZhdjZ3TGhnVi9KK1NYdlBlUng2aDFmb0lsVUF4WDJMdDFSOEduZApNejhqdHJxN29ycE1EQU9xOHNyaGxzZkZDYnJtUFZKSnNTd1J4R3ZJN3ZLTm54VXRXVFE9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== \ No newline at end of file diff --git a/internal/config/test_assets/k9s.yml b/internal/config/test_assets/k9s.yml new file mode 100644 index 00000000..189706a6 --- /dev/null +++ b/internal/config/test_assets/k9s.yml @@ -0,0 +1,28 @@ +k9s: + refreshRate: 2 + logBufferSize: 200 + currentContext: minikube + currentCluster: minikube + clusters: + minikube: + namespace: + active: kube-system + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: ctx + fred: + namespace: + active: default + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: po \ No newline at end of file diff --git a/config/test_assets/k9s1.yml b/internal/config/test_assets/k9s1.yml similarity index 100% rename from config/test_assets/k9s1.yml rename to internal/config/test_assets/k9s1.yml diff --git a/config/test_assets/k9s_old.yml b/internal/config/test_assets/k9s_old.yml similarity index 100% rename from config/test_assets/k9s_old.yml rename to internal/config/test_assets/k9s_old.yml diff --git a/internal/config/test_assets/k9s_toast.yml b/internal/config/test_assets/k9s_toast.yml new file mode 100644 index 00000000..189706a6 --- /dev/null +++ b/internal/config/test_assets/k9s_toast.yml @@ -0,0 +1,28 @@ +k9s: + refreshRate: 2 + logBufferSize: 200 + currentContext: minikube + currentCluster: minikube + clusters: + minikube: + namespace: + active: kube-system + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: ctx + fred: + namespace: + active: default + favorites: + - default + - kube-public + - istio-system + - all + - kube-system + view: + active: po \ No newline at end of file diff --git a/config/view.go b/internal/config/view.go similarity index 70% rename from config/view.go rename to internal/config/view.go index 6cf3eebf..e9461c83 100644 --- a/config/view.go +++ b/internal/config/view.go @@ -7,11 +7,13 @@ type View struct { Active string `yaml:"active"` } +// NewView creates a new view configuration. func NewView() *View { return &View{Active: defaultView} } -func (v *View) Validate(ClusterInfo) { +// Validate a view configuration. +func (v *View) Validate() { if len(v.Active) == 0 { v.Active = defaultView } diff --git a/config/view_test.go b/internal/config/view_test.go similarity index 56% rename from config/view_test.go rename to internal/config/view_test.go index 1644d203..2fc7532a 100644 --- a/config/view_test.go +++ b/internal/config/view_test.go @@ -3,18 +3,23 @@ package config_test import ( "testing" - "github.com/derailed/k9s/config" + "github.com/derailed/k9s/internal/config" "github.com/stretchr/testify/assert" ) func TestViewValidate(t *testing.T) { v := config.NewView() - ci := NewMockClusterInfo() - v.Validate(ci) + v.Validate() assert.Equal(t, "po", v.Active) v.Active = "fred" - v.Validate(ci) + v.Validate() assert.Equal(t, "fred", v.Active) } + +func TestViewValidateBlank(t *testing.T) { + var v config.View + v.Validate() + assert.Equal(t, "po", v.Active) +} diff --git a/resource/k8s/api.go b/internal/k8s/api.go similarity index 63% rename from resource/k8s/api.go rename to internal/k8s/api.go index 8efaba89..6412250c 100644 --- a/resource/k8s/api.go +++ b/internal/k8s/api.go @@ -1,17 +1,13 @@ package k8s import ( - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmd "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/kubernetes/pkg/kubectl/metricsutil" - "k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metricsapi "k8s.io/metrics/pkg/apis/metrics" versioned "k8s.io/metrics/pkg/client/clientset/versioned" ) @@ -20,13 +16,13 @@ import ( const NA = "n/a" var ( - conn connection = &apiServer{} - supportedMetricsAPIVersions = []string{"v1beta1"} + conn = &apiServer{} + supportedMetricsAPIVersions = []string{"v1beta1"} ) type ( - // ApiGroup represents a K8s resource descriptor. - ApiGroup struct { + // APIGroup represents a K8s resource descriptor. + APIGroup struct { Resource string Group, Kind, Version string Plural, Singular string @@ -53,31 +49,25 @@ type ( nsDialOrDie() dynamic.NamespaceableResourceInterface mxsDial() (*versioned.Clientset, error) heapsterDial() (*metricsutil.HeapsterMetricsClient, error) - getClusterName() string hasMetricsServer() bool } apiServer struct { - flags *genericclioptions.ConfigFlags + config *Config client kubernetes.Interface dClient dynamic.Interface - csClient *clientset.Clientset nsClient dynamic.NamespaceableResourceInterface heapsterClient *metricsutil.HeapsterMetricsClient mxsClient *versioned.Clientset - clusterName string useMetricServer bool } ) -// InitConnection initialize connection from command line args. -func InitConnection(flags *genericclioptions.ConfigFlags) { - conn = &apiServer{flags: flags} -} - -func (a *apiServer) getClusterName() string { - a.checkCurrentConfig() - return a.clusterName +// InitConnectionOrDie initialize connection from command line args. +// Checks for connectivity with the api server. +func InitConnectionOrDie(config *Config) { + conn = &apiServer{config: config} + conn.useMetricServer = conn.supportsMxServer() } func (a *apiServer) hasMetricsServer() bool { @@ -86,32 +76,20 @@ func (a *apiServer) hasMetricsServer() bool { // ActiveClusterOrDie Fetch the current cluster based on current context. func ActiveClusterOrDie() string { - cfg := conn.apiConfigOrDie() - return cfg.Contexts[cfg.CurrentContext].Cluster -} - -// AllClustersOrDie fetch all available clusters from config. -func AllClustersOrDie() []string { - cfg := conn.apiConfigOrDie() - - cc := make([]string, 0, len(cfg.Clusters)) - for k := range cfg.Clusters { - cc = append(cc, k) - } - return cc -} - -// AllNamespacesOrDie fetch all available namespaces on current cluster. -func AllNamespacesOrDie() []string { - nn, err := NewNamespace().List("") + cl, err := conn.config.CurrentClusterName() if err != nil { panic(err) } - ss := make([]string, len(nn)) - for i, n := range nn { - ss[i] = n.(v1.Namespace).Name + return cl +} + +// AllClusterNamesOrDie fetch all available clusters from config. +func AllClusterNamesOrDie() []string { + if cc, err := conn.config.ClusterNames(); err != nil { + panic(err) + } else { + return cc } - return ss } // DialOrDie returns a handle to api server or die. @@ -125,26 +103,9 @@ func (a *apiServer) dialOrDie() kubernetes.Interface { if a.client, err = kubernetes.NewForConfig(a.restConfigOrDie()); err != nil { panic(err) } - return a.client } -func (a *apiServer) csDialOrDie() *clientset.Clientset { - a.checkCurrentConfig() - if a.csClient != nil { - return a.csClient - } - - var cfg *rest.Config - // cfg := clientcmd.NewNonInteractiveClientConfig(config, contextName, overrides, configAccess) - var err error - if a.csClient, err = clientset.NewForConfig(cfg); err != nil { - panic(err) - } - - return a.csClient -} - // DynDial returns a handle to the api server. func (a *apiServer) dynDialOrDie() dynamic.Interface { a.checkCurrentConfig() @@ -201,56 +162,54 @@ func (a *apiServer) mxsDial() (*versioned.Clientset, error) { return a.mxsClient, err } -// ConfigOrDie check kubernetes cluster config. -// Dies if no config is found or incorrect. -func ConfigOrDie() { - cfg := conn.apiConfigOrDie() - if clientcmdapi.IsConfigEmpty(&cfg) { - panic("K8s config file load failed. Please check your .kube/config or $KUBECONFIG env") - } -} - func (a *apiServer) configAccess() clientcmd.ConfigAccess { - return a.flags.ToRawKubeConfigLoader().ConfigAccess() -} - -func (a *apiServer) apiConfigOrDie() clientcmdapi.Config { - c, err := a.flags.ToRawKubeConfigLoader().RawConfig() - if err != nil { - panic(err) - } - return c -} - -func (a *apiServer) restConfigOrDie() *restclient.Config { - cfg, err := a.flags.ToRESTConfig() + cfg, err := a.config.ConfigAccess() if err != nil { panic(err) } return cfg } -func (a *apiServer) checkCurrentConfig() { - cfg := a.apiConfigOrDie() - currentCluster := cfg.Contexts[cfg.CurrentContext].Cluster +func (a *apiServer) restConfigOrDie() *restclient.Config { + cfg, err := a.config.RESTConfig() + if err != nil { + panic(err) + } + return cfg +} - if len(a.clusterName) == 0 { - a.clusterName = currentCluster - a.useMetricServer = a.supportsMxServer() - return +func (a *apiServer) switchContextOrDie(ctx string) { + currentCtx, err := a.config.CurrentContextName() + if err != nil { + panic(err) } - if a.clusterName != currentCluster { + if currentCtx != ctx { a.reset() - a.clusterName = currentCluster + if err := a.config.SwitchContext(ctx); err != nil { + panic(err) + } a.useMetricServer = a.supportsMxServer() } } +func (a *apiServer) checkCurrentConfig() { + // currentCluster, err := a.config.CurrentCluster() + // if err != nil { + // panic(err) + // } + + // if a.clusterName != currentCluster { + // a.reset() + // a.clusterName = currentCluster + // a.useMetricServer = a.supportsMxServer() + // return + // } +} + func (a *apiServer) reset() { a.client, a.dClient, a.nsClient = nil, nil, nil a.heapsterClient, a.mxsClient = nil, nil - a.clusterName = "" } func (a *apiServer) supportsMxServer() bool { diff --git a/internal/k8s/assets/config b/internal/k8s/assets/config new file mode 100644 index 00000000..5541a687 --- /dev/null +++ b/internal/k8s/assets/config @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Config +preferences: {} +clusters: +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3000 + name: fred +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3001 + name: blee +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3002 + name: duh +contexts: +- context: + cluster: fred + user: fred + name: fred +- context: + cluster: blee + user: blee + name: blee +- context: + cluster: duh + user: duh + name: duh +current-context: fred +users: +- name: fred + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== +- name: blee + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== +- name: duh + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== diff --git a/internal/k8s/assets/config.1 b/internal/k8s/assets/config.1 new file mode 100644 index 00000000..9c2ff1e3 --- /dev/null +++ b/internal/k8s/assets/config.1 @@ -0,0 +1,39 @@ +apiVersion: v1 +clusters: +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3001 + name: blee +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3002 + name: duh +- cluster: + insecure-skip-tls-verify: true + server: https://localhost:3000 + name: fred +contexts: +- context: + cluster: blee + user: blee + name: blee +- context: + cluster: duh + user: duh + name: duh +current-context: fred +kind: Config +preferences: {} +users: +- name: blee + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== +- name: duh + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== +- name: fred + user: + client-certificate-data: ZnJlZA== + client-key-data: ZnJlZA== diff --git a/internal/k8s/cluster.go b/internal/k8s/cluster.go new file mode 100644 index 00000000..5a367aaa --- /dev/null +++ b/internal/k8s/cluster.go @@ -0,0 +1,45 @@ +package k8s + +// Cluster manages a Kubernetes ClusterRole. +type Cluster struct{} + +// NewCluster instantiates a new ClusterRole. +func NewCluster() *Cluster { + return &Cluster{} +} + +// Version returns the current cluster git version. +func (c *Cluster) Version() (string, error) { + rev, err := conn.dialOrDie().Discovery().ServerVersion() + if err != nil { + return "", err + } + return rev.GitVersion, nil +} + +// ContextName returns the currently active context. +func (c *Cluster) ContextName() string { + ctx, err := conn.config.CurrentContextName() + if err != nil { + return "N/A" + } + return ctx +} + +// ClusterName return the currently active cluster name. +func (c *Cluster) ClusterName() string { + ctx, err := conn.config.CurrentClusterName() + if err != nil { + return "N/A" + } + return ctx +} + +// UserName returns the currently active user. +func (c *Cluster) UserName() string { + usr, err := conn.config.CurrentUserName() + if err != nil { + return "N/A" + } + return usr +} diff --git a/resource/k8s/cluster_role.go b/internal/k8s/cluster_role.go similarity index 100% rename from resource/k8s/cluster_role.go rename to internal/k8s/cluster_role.go diff --git a/resource/k8s/cluster_roleb.go b/internal/k8s/cluster_roleb.go similarity index 100% rename from resource/k8s/cluster_roleb.go rename to internal/k8s/cluster_roleb.go diff --git a/resource/k8s/cm.go b/internal/k8s/cm.go similarity index 100% rename from resource/k8s/cm.go rename to internal/k8s/cm.go diff --git a/internal/k8s/config.go b/internal/k8s/config.go new file mode 100644 index 00000000..860b6905 --- /dev/null +++ b/internal/k8s/config.go @@ -0,0 +1,260 @@ +package k8s + +import ( + "errors" + "fmt" + + log "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + "k8s.io/cli-runtime/pkg/genericclioptions" + restclient "k8s.io/client-go/rest" + clientcmd "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" +) + +// Config tracks a kubernetes configuration. +type Config struct { + flags *genericclioptions.ConfigFlags + clientConfig clientcmd.ClientConfig + currentContext string + rawConfig *clientcmdapi.Config + restConfig *restclient.Config +} + +// NewConfig returns a new k8s config or an error if the flags are invalid. +func NewConfig(f *genericclioptions.ConfigFlags) *Config { + return &Config{flags: f} +} + +// SwitchContext changes the kubeconfig context to a new cluster. +func (c *Config) SwitchContext(name string) error { + currentCtx, err := c.CurrentContextName() + if err != nil { + return err + } + + if currentCtx != name { + c.reset() + c.flags.Context, c.currentContext = &name, name + } + return nil +} + +func (c *Config) reset() { + c.clientConfig, c.rawConfig, c.restConfig = nil, nil, nil +} + +// CurrentContextName returns the currently active config context. +func (c *Config) CurrentContextName() (string, error) { + if isSet(c.flags.Context) { + return *c.flags.Context, nil + } + cfg, err := c.RawConfig() + if err != nil { + return "", err + } + return cfg.CurrentContext, nil +} + +// GetContext fetch a given context or error if it does not exists. +func (c *Config) GetContext(n string) (*clientcmdapi.Context, error) { + cfg, err := c.RawConfig() + if err != nil { + return nil, err + } + + if c, ok := cfg.Contexts[n]; ok { + return c, nil + } + return nil, fmt.Errorf("invalid context `%s specified", n) +} + +// Contexts fetch all available contexts. +func (c *Config) Contexts() (map[string]*clientcmdapi.Context, error) { + var cc map[string]*clientcmdapi.Context + cfg, err := c.RawConfig() + if err != nil { + return cc, err + } + return cfg.Contexts, nil +} + +// DelContext remove a given context from the configuration. +func (c *Config) DelContext(n string) error { + cfg, err := c.RawConfig() + if err != nil { + return err + } + delete(cfg.Contexts, n) + return clientcmd.ModifyConfig(c.clientConfig.ConfigAccess(), cfg, true) +} + +// ContextNames fetch all available contexts. +func (c *Config) ContextNames() ([]string, error) { + var cc []string + cfg, err := c.RawConfig() + if err != nil { + return cc, err + } + + cc = make([]string, 0, len(cfg.Contexts)) + for n := range cfg.Contexts { + cc = append(cc, n) + } + return cc, nil +} + +// ClusterNameFromContext returns the cluster associated with the given context. +func (c *Config) ClusterNameFromContext(ctx string) (string, error) { + cfg, err := c.RawConfig() + if err != nil { + return "", err + } + if ctx, ok := cfg.Contexts[ctx]; ok { + return ctx.Cluster, nil + } + return "", fmt.Errorf("unable to locate cluster from context %s", ctx) +} + +// CurrentClusterName returns the active cluster name. +func (c *Config) CurrentClusterName() (string, error) { + if isSet(c.flags.ClusterName) { + return *c.flags.ClusterName, nil + } + + cfg, err := c.RawConfig() + if err != nil { + return "", err + } + current := cfg.CurrentContext + if isSet(c.flags.Context) { + current = *c.flags.Context + } + if ctx, ok := cfg.Contexts[current]; ok { + return ctx.Cluster, nil + } + return "", errors.New("unable to locate current cluster") +} + +// ClusterNames fetch all kubeconfig defined clusters. +func (c *Config) ClusterNames() ([]string, error) { + var cc []string + if err := c.configFromFlags(); err != nil { + return cc, err + } + + cfg, err := c.RawConfig() + if err != nil { + return cc, err + } + + cc = make([]string, 0, len(cfg.Clusters)) + for name := range cfg.Clusters { + cc = append(cc, name) + } + return cc, nil +} + +// CurrentUserName retrieves the active user name. +func (c *Config) CurrentUserName() (string, error) { + if isSet(c.flags.AuthInfoName) { + return *c.flags.AuthInfoName, nil + } + + cfg, err := c.RawConfig() + if err != nil { + return "", err + } + + current := cfg.CurrentContext + if isSet(c.flags.Context) { + current = *c.flags.Context + } + if ctx, ok := cfg.Contexts[current]; ok { + return ctx.AuthInfo, nil + } + return "", errors.New("unable to locate current cluster") +} + +// CurrentNamespaceName retrieves the active namespace. +func (c *Config) CurrentNamespaceName() string { + if isSet(c.flags.Namespace) { + return *c.flags.Namespace + } + return "default" +} + +// NamespaceNames fetch all available namespaces on current cluster. +func (c *Config) NamespaceNames() ([]string, error) { + var nn []string + ll, err := NewNamespace().List("") + if err != nil { + return nn, err + } + nn = make([]string, len(nn)) + for i, n := range ll { + nn[i] = n.(v1.Namespace).Name + } + return nn, nil +} + +// ConfigAccess return the current kubeconfig api server access configuration. +func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) { + var acc clientcmd.ConfigAccess + if err := c.configFromFlags(); err != nil { + return acc, err + } + return c.clientConfig.ConfigAccess(), nil +} + +// RawConfig fetch the current kubeconfig with no overrides. +func (c *Config) RawConfig() (clientcmdapi.Config, error) { + if c.rawConfig != nil && c.rawConfig.CurrentContext != c.currentContext { + log.Debugf("Context swith detected...") + c.currentContext = c.rawConfig.CurrentContext + c.reset() + } + + if c.rawConfig == nil { + if err := c.configFromFlags(); err != nil { + return clientcmdapi.Config{}, err + } + log.Debugf("Reloading RawConfig...") + cfg, err := c.clientConfig.RawConfig() + if err != nil { + return cfg, err + } + c.rawConfig = &cfg + } + return *c.rawConfig, nil +} + +// RESTConfig fetch the current REST api service connection. +func (c *Config) RESTConfig() (*restclient.Config, error) { + var err error + if c.restConfig == nil { + if err = c.configFromFlags(); err != nil { + return nil, err + } + c.restConfig, err = c.flags.ToRESTConfig() + if err != nil { + return c.restConfig, err + } + log.Debugf("Connecting to API Server %s", c.restConfig.Host) + } + return c.restConfig, nil +} + +// ---------------------------------------------------------------------------- +// Helpers... + +func (c *Config) configFromFlags() error { + if c.clientConfig == nil { + c.clientConfig = c.flags.ToRawKubeConfigLoader() + } + return nil +} + +func isSet(s *string) bool { + return s != nil && len(*s) != 0 +} diff --git a/internal/k8s/config_test.go b/internal/k8s/config_test.go new file mode 100644 index 00000000..b9c486c0 --- /dev/null +++ b/internal/k8s/config_test.go @@ -0,0 +1,221 @@ +package k8s_test + +import ( + "errors" + "testing" + + "github.com/derailed/k9s/internal/k8s" + "github.com/stretchr/testify/assert" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +func TestConfigCurrentContext(t *testing.T) { + name, kubeConfig := "blee", "./assets/config" + uu := []struct { + flags *genericclioptions.ConfigFlags + context string + }{ + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "fred"}, + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig, Context: &name}, "blee"}, + } + + for _, u := range uu { + cfg := k8s.NewConfig(u.flags) + ctx, err := cfg.CurrentContextName() + assert.Nil(t, err) + assert.Equal(t, u.context, ctx) + } +} + +func TestConfigCurrentCluster(t *testing.T) { + name, kubeConfig := "blee", "./assets/config" + uu := []struct { + flags *genericclioptions.ConfigFlags + cluster string + }{ + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "fred"}, + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig, ClusterName: &name}, "blee"}, + } + + for _, u := range uu { + cfg := k8s.NewConfig(u.flags) + ctx, err := cfg.CurrentClusterName() + assert.Nil(t, err) + assert.Equal(t, u.cluster, ctx) + } +} + +func TestConfigCurrentUser(t *testing.T) { + name, kubeConfig := "blee", "./assets/config" + uu := []struct { + flags *genericclioptions.ConfigFlags + user string + }{ + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "fred"}, + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig, AuthInfoName: &name}, "blee"}, + } + + for _, u := range uu { + cfg := k8s.NewConfig(u.flags) + ctx, err := cfg.CurrentUserName() + assert.Nil(t, err) + assert.Equal(t, u.user, ctx) + } +} + +func TestConfigCurrentNamespace(t *testing.T) { + name, kubeConfig := "blee", "./assets/config" + uu := []struct { + flags *genericclioptions.ConfigFlags + namespace string + }{ + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "default"}, + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig, Namespace: &name}, "blee"}, + } + + for _, u := range uu { + cfg := k8s.NewConfig(u.flags) + assert.Equal(t, u.namespace, cfg.CurrentNamespaceName()) + } +} + +func TestConfigGetContext(t *testing.T) { + kubeConfig := "./assets/config" + uu := []struct { + flags *genericclioptions.ConfigFlags + cluster string + err error + }{ + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "blee", nil}, + {&genericclioptions.ConfigFlags{KubeConfig: &kubeConfig}, "bozo", errors.New("invalid context `bozo specified")}, + } + + for _, u := range uu { + cfg := k8s.NewConfig(u.flags) + ctx, err := cfg.GetContext(u.cluster) + if err != nil { + assert.Equal(t, u.err, err) + } else { + assert.NotNil(t, ctx) + assert.Equal(t, u.cluster, ctx.Cluster) + } + } +} + +func TestConfigSwitchContext(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + err := cfg.SwitchContext("blee") + assert.Nil(t, err) + ctx, err := cfg.CurrentContextName() + assert.Nil(t, err) + assert.Equal(t, "blee", ctx) +} + +func TestConfigClusterNameFromContext(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + cl, err := cfg.ClusterNameFromContext("blee") + assert.Nil(t, err) + assert.Equal(t, "blee", cl) +} + +func TestConfigAccess(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + acc, err := cfg.ConfigAccess() + assert.Nil(t, err) + assert.True(t, len(acc.GetDefaultFilename()) > 0) +} + +func TestConfigContexts(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + cc, err := cfg.Contexts() + assert.Nil(t, err) + assert.Equal(t, 3, len(cc)) +} + +func TestConfigContextNames(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + cc, err := cfg.ContextNames() + assert.Nil(t, err) + assert.Equal(t, 3, len(cc)) +} + +func TestConfigClusterNames(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + cc, err := cfg.ClusterNames() + assert.Nil(t, err) + assert.Equal(t, 3, len(cc)) +} + +func TestConfigDelContext(t *testing.T) { + cluster, kubeConfig := "duh", "./assets/config.1" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + ClusterName: &cluster, + } + + cfg := k8s.NewConfig(&flags) + err := cfg.DelContext("fred") + assert.Nil(t, err) + cc, err := cfg.ContextNames() + assert.Nil(t, err) + assert.Equal(t, 2, len(cc)) +} + +func TestConfigRestConfig(t *testing.T) { + kubeConfig := "./assets/config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + } + + cfg := k8s.NewConfig(&flags) + rc, err := cfg.RESTConfig() + assert.Nil(t, err) + assert.Equal(t, "https://localhost:3000", rc.Host) +} + +func TestConfigBadConfig(t *testing.T) { + kubeConfig := "./assets/bork_config" + flags := genericclioptions.ConfigFlags{ + KubeConfig: &kubeConfig, + } + + cfg := k8s.NewConfig(&flags) + _, err := cfg.RESTConfig() + assert.NotNil(t, err) +} diff --git a/resource/k8s/context.go b/internal/k8s/context.go similarity index 58% rename from resource/k8s/context.go rename to internal/k8s/context.go index c5190c71..ee4e4af4 100644 --- a/resource/k8s/context.go +++ b/internal/k8s/context.go @@ -3,7 +3,6 @@ package k8s import ( "fmt" - "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" ) @@ -19,9 +18,13 @@ type NamedContext struct { Context *api.Context } -// CurrentCluster return active cluster name -func (c *NamedContext) CurrentCluster() string { - return conn.getClusterName() +// MustCurrentClusterName return the active cluster name. +func (c *NamedContext) MustCurrentClusterName() string { + cl, err := conn.config.CurrentClusterName() + if err != nil { + panic(err) + } + return cl } // Context represents a Kubernetes Context. @@ -34,17 +37,21 @@ func NewContext() Res { // Get a Context. func (*Context) Get(_, n string) (interface{}, error) { - return &NamedContext{ - Name: n, - Context: conn.apiConfigOrDie().Contexts[n], - }, nil + ctx, err := conn.config.GetContext(n) + if err != nil { + return nil, err + } + return &NamedContext{Name: n, Context: ctx}, nil } // List all Contexts in a given namespace func (*Context) List(string) (Collection, error) { - con := conn.apiConfigOrDie() - cc := make([]interface{}, 0, len(con.Contexts)) - for k, v := range con.Contexts { + ctxs, err := conn.config.Contexts() + if err != nil { + return Collection{}, err + } + cc := make([]interface{}, 0, len(ctxs)) + for k, v := range ctxs { cc = append(cc, &NamedContext{k, v}) } return cc, nil @@ -52,18 +59,18 @@ func (*Context) List(string) (Collection, error) { // Delete a Context func (*Context) Delete(_, n string) error { - con := conn.apiConfigOrDie() - if con.CurrentContext == n { + ctx, err := conn.config.CurrentContextName() + if err != nil { + return err + } + if ctx == n { return fmt.Errorf("trying to delete your current context %s", n) } - - delete(con.Contexts, n) - return clientcmd.ModifyConfig(conn.configAccess(), con, true) + return conn.config.DelContext(n) } // Switch cluster Context. func (*Context) Switch(n string) error { - con := conn.apiConfigOrDie() - con.CurrentContext = n - return clientcmd.ModifyConfig(conn.configAccess(), con, true) + conn.switchContextOrDie(n) + return nil } diff --git a/resource/k8s/crd.go b/internal/k8s/crd.go similarity index 100% rename from resource/k8s/crd.go rename to internal/k8s/crd.go diff --git a/resource/k8s/cronjob.go b/internal/k8s/cronjob.go similarity index 100% rename from resource/k8s/cronjob.go rename to internal/k8s/cronjob.go diff --git a/resource/k8s/dp.go b/internal/k8s/dp.go similarity index 100% rename from resource/k8s/dp.go rename to internal/k8s/dp.go diff --git a/resource/k8s/ds.go b/internal/k8s/ds.go similarity index 100% rename from resource/k8s/ds.go rename to internal/k8s/ds.go diff --git a/resource/k8s/ep.go b/internal/k8s/ep.go similarity index 100% rename from resource/k8s/ep.go rename to internal/k8s/ep.go diff --git a/resource/k8s/evt.go b/internal/k8s/evt.go similarity index 100% rename from resource/k8s/evt.go rename to internal/k8s/evt.go diff --git a/resource/k8s/hpa.go b/internal/k8s/hpa.go similarity index 100% rename from resource/k8s/hpa.go rename to internal/k8s/hpa.go diff --git a/resource/k8s/ing.go b/internal/k8s/ing.go similarity index 100% rename from resource/k8s/ing.go rename to internal/k8s/ing.go diff --git a/resource/k8s/job.go b/internal/k8s/job.go similarity index 100% rename from resource/k8s/job.go rename to internal/k8s/job.go diff --git a/resource/k8s/metrics.go b/internal/k8s/metrics.go similarity index 100% rename from resource/k8s/metrics.go rename to internal/k8s/metrics.go diff --git a/resource/k8s/no.go b/internal/k8s/no.go similarity index 100% rename from resource/k8s/no.go rename to internal/k8s/no.go diff --git a/resource/k8s/ns.go b/internal/k8s/ns.go similarity index 100% rename from resource/k8s/ns.go rename to internal/k8s/ns.go diff --git a/resource/k8s/pod.go b/internal/k8s/pod.go similarity index 100% rename from resource/k8s/pod.go rename to internal/k8s/pod.go diff --git a/resource/k8s/pv.go b/internal/k8s/pv.go similarity index 100% rename from resource/k8s/pv.go rename to internal/k8s/pv.go diff --git a/resource/k8s/pvc.go b/internal/k8s/pvc.go similarity index 100% rename from resource/k8s/pvc.go rename to internal/k8s/pvc.go diff --git a/resource/k8s/rc.go b/internal/k8s/rc.go similarity index 100% rename from resource/k8s/rc.go rename to internal/k8s/rc.go diff --git a/resource/k8s/resource.go b/internal/k8s/resource.go similarity index 100% rename from resource/k8s/resource.go rename to internal/k8s/resource.go diff --git a/resource/k8s/role.go b/internal/k8s/role.go similarity index 100% rename from resource/k8s/role.go rename to internal/k8s/role.go diff --git a/resource/k8s/role_binding.go b/internal/k8s/role_binding.go similarity index 100% rename from resource/k8s/role_binding.go rename to internal/k8s/role_binding.go diff --git a/resource/k8s/rs.go b/internal/k8s/rs.go similarity index 100% rename from resource/k8s/rs.go rename to internal/k8s/rs.go diff --git a/resource/k8s/sa.go b/internal/k8s/sa.go similarity index 100% rename from resource/k8s/sa.go rename to internal/k8s/sa.go diff --git a/resource/k8s/secret.go b/internal/k8s/secret.go similarity index 100% rename from resource/k8s/secret.go rename to internal/k8s/secret.go diff --git a/resource/k8s/sts.go b/internal/k8s/sts.go similarity index 100% rename from resource/k8s/sts.go rename to internal/k8s/sts.go diff --git a/resource/k8s/svc.go b/internal/k8s/svc.go similarity index 100% rename from resource/k8s/svc.go rename to internal/k8s/svc.go diff --git a/resource/base.go b/internal/resource/base.go similarity index 97% rename from resource/base.go rename to internal/resource/base.go index 141316ef..78061efb 100644 --- a/resource/base.go +++ b/internal/resource/base.go @@ -3,7 +3,7 @@ package resource import ( "path" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/resource/cluster.go b/internal/resource/cluster.go similarity index 73% rename from resource/cluster.go rename to internal/resource/cluster.go index c2eb557a..6806c2df 100644 --- a/resource/cluster.go +++ b/internal/resource/cluster.go @@ -1,15 +1,17 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" - "k8s.io/api/core/v1" + "github.com/derailed/k9s/internal/k8s" + v1 "k8s.io/api/core/v1" ) type ( // ClusterIfc represents a cluster. ClusterIfc interface { Version() (string, error) + ContextName() string ClusterName() string + UserName() string } // MetricsIfc represents a metrics server. @@ -45,11 +47,21 @@ func (c *Cluster) Version() string { return info } -// Name returns the cluster name -func (c *Cluster) Name() string { +// ContextName returns the context name. +func (c *Cluster) ContextName() string { + return c.api.ContextName() +} + +// ClusterName returns the cluster name. +func (c *Cluster) ClusterName() string { return c.api.ClusterName() } +// UserName returns the user name. +func (c *Cluster) UserName() string { + return c.api.UserName() +} + // Metrics gathers node level metrics and compute utilization percentages. func (c *Cluster) Metrics() (k8s.Metric, error) { return c.mx.NodeMetrics() diff --git a/resource/cluster_test.go b/internal/resource/cluster_test.go similarity index 88% rename from resource/cluster_test.go rename to internal/resource/cluster_test.go index 00ab49dc..6afaa602 100644 --- a/resource/cluster_test.go +++ b/internal/resource/cluster_test.go @@ -4,10 +4,9 @@ import ( "fmt" "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" + "github.com/derailed/k9s/internal/resource" m "github.com/petergtz/pegomock" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) @@ -38,7 +37,7 @@ func TestClusterName(t *testing.T) { m.When(cIfc.ClusterName()).ThenReturn("fred") ci := resource.NewClusterWithArgs(cIfc, mxIfc) - assert.Equal(t, "fred", ci.Name()) + assert.Equal(t, "fred", ci.ClusterName()) } func TestClusterMetrics(t *testing.T) { @@ -58,7 +57,7 @@ func TestClusterMetrics(t *testing.T) { func setup(t *testing.T) { m.RegisterMockTestingT(t) m.RegisterMockFailHandler(func(m string, i ...int) { - log.Println("Boom!", m, i) + fmt.Println("Boom!", m, i) }) } diff --git a/resource/cm.go b/internal/resource/cm.go similarity index 97% rename from resource/cm.go rename to internal/resource/cm.go index 585fda25..f906f7dc 100644 --- a/resource/cm.go +++ b/internal/resource/cm.go @@ -4,9 +4,9 @@ import ( "log" "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" yaml "gopkg.in/yaml.v2" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" ) // ConfigMap tracks a kubernetes resource. diff --git a/resource/cm_test.go b/internal/resource/cm_test.go similarity index 98% rename from resource/cm_test.go rename to internal/resource/cm_test.go index 958ffef8..59d7d985 100644 --- a/resource/cm_test.go +++ b/internal/resource/cm_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/context.go b/internal/resource/context.go similarity index 96% rename from resource/context.go rename to internal/resource/context.go index 6f83698a..35cfd52f 100644 --- a/resource/context.go +++ b/internal/resource/context.go @@ -1,7 +1,7 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" ) @@ -79,7 +79,7 @@ func (r *Context) Fields(ns string) Row { i := r.instance name := i.Name - if i.CurrentCluster() == name { + if i.MustCurrentClusterName() == name { name += "*" } diff --git a/resource/context_test.go b/internal/resource/context_test.go similarity index 97% rename from resource/context_test.go rename to internal/resource/context_test.go index 5938d8ed..d288b3c6 100644 --- a/resource/context_test.go +++ b/internal/resource/context_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/client-go/tools/clientcmd/api" diff --git a/resource/cr.go b/internal/resource/cr.go similarity index 98% rename from resource/cr.go rename to internal/resource/cr.go index 14d7c3df..34b34f84 100644 --- a/resource/cr.go +++ b/internal/resource/cr.go @@ -1,7 +1,7 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "k8s.io/api/rbac/v1" diff --git a/resource/cr_binding.go b/internal/resource/cr_binding.go similarity index 98% rename from resource/cr_binding.go rename to internal/resource/cr_binding.go index 01baf990..6c35e77b 100644 --- a/resource/cr_binding.go +++ b/internal/resource/cr_binding.go @@ -1,7 +1,7 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/rbac/v1" diff --git a/resource/cr_binding_test.go b/internal/resource/cr_binding_test.go similarity index 96% rename from resource/cr_binding_test.go rename to internal/resource/cr_binding_test.go index 6aae78e9..38abb831 100644 --- a/resource/cr_binding_test.go +++ b/internal/resource/cr_binding_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" rbacv1 "k8s.io/api/rbac/v1" diff --git a/resource/cr_test.go b/internal/resource/cr_test.go similarity index 96% rename from resource/cr_test.go rename to internal/resource/cr_test.go index 74ecb1d1..d50e56fc 100644 --- a/resource/cr_test.go +++ b/internal/resource/cr_test.go @@ -1,14 +1,14 @@ package resource_test import ( + "fmt" "strings" "testing" "time" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" + "github.com/derailed/k9s/internal/resource" m "github.com/petergtz/pegomock" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -122,7 +122,7 @@ func newClusterRole() resource.Columnar { func testTime() time.Time { t, err := time.Parse(time.RFC3339, "2018-12-14T10:36:43.326972-07:00") if err != nil { - log.Println("TestTime Failed", err) + fmt.Println("TestTime Failed", err) } return t } diff --git a/resource/crd.go b/internal/resource/crd.go similarity index 98% rename from resource/crd.go rename to internal/resource/crd.go index 97ed0d1c..ba5ce999 100644 --- a/resource/crd.go +++ b/internal/resource/crd.go @@ -3,7 +3,7 @@ package resource import ( "time" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/resource/crd_test.go b/internal/resource/crd_test.go similarity index 97% rename from resource/crd_test.go rename to internal/resource/crd_test.go index 162af9e1..464dc7a7 100644 --- a/resource/crd_test.go +++ b/internal/resource/crd_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/resource/cronjob.go b/internal/resource/cronjob.go similarity index 98% rename from resource/cronjob.go rename to internal/resource/cronjob.go index f3a53b15..5027b1b9 100644 --- a/resource/cronjob.go +++ b/internal/resource/cronjob.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" batchv1beta1 "k8s.io/api/batch/v1beta1" diff --git a/resource/cronjob_test.go b/internal/resource/cronjob_test.go similarity index 98% rename from resource/cronjob_test.go rename to internal/resource/cronjob_test.go index 895cb98d..2fc69344 100644 --- a/resource/cronjob_test.go +++ b/internal/resource/cronjob_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" batchv1beta1 "k8s.io/api/batch/v1beta1" diff --git a/resource/custom.go b/internal/resource/custom.go similarity index 96% rename from resource/custom.go rename to internal/resource/custom.go index 33a4e3f7..7f5b5126 100644 --- a/resource/custom.go +++ b/internal/resource/custom.go @@ -7,7 +7,7 @@ import ( "path" "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" @@ -153,13 +153,13 @@ func (*Custom) ExtFields() Properties { return Properties{} } -func getCRDS() map[string]k8s.ApiGroup { - m := map[string]k8s.ApiGroup{} +func getCRDS() map[string]k8s.APIGroup { + m := map[string]k8s.APIGroup{} list := NewCRDList("") ll, _ := list.Resource().List("") for _, l := range ll { ff := l.ExtFields() - grp := k8s.ApiGroup{ + grp := k8s.APIGroup{ Resource: ff["name"].(string), Version: ff["version"].(string), Group: ff["group"].(string), diff --git a/resource/dp.go b/internal/resource/dp.go similarity index 98% rename from resource/dp.go rename to internal/resource/dp.go index eb024baa..9411f0c9 100644 --- a/resource/dp.go +++ b/internal/resource/dp.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/apps/v1" diff --git a/resource/dp_test.go b/internal/resource/dp_test.go similarity index 98% rename from resource/dp_test.go rename to internal/resource/dp_test.go index 1612416e..f0a83f93 100644 --- a/resource/dp_test.go +++ b/internal/resource/dp_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" diff --git a/resource/ds.go b/internal/resource/ds.go similarity index 98% rename from resource/ds.go rename to internal/resource/ds.go index f7e7f1fa..768eec66 100644 --- a/resource/ds.go +++ b/internal/resource/ds.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" extv1beta1 "k8s.io/api/extensions/v1beta1" diff --git a/resource/ds_test.go b/internal/resource/ds_test.go similarity index 98% rename from resource/ds_test.go rename to internal/resource/ds_test.go index fde6511b..73a5c2fd 100644 --- a/resource/ds_test.go +++ b/internal/resource/ds_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" extv1beta1 "k8s.io/api/extensions/v1beta1" diff --git a/resource/ep.go b/internal/resource/ep.go similarity index 98% rename from resource/ep.go rename to internal/resource/ep.go index 9b34b160..125a1d6d 100644 --- a/resource/ep.go +++ b/internal/resource/ep.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/ep_test.go b/internal/resource/ep_test.go similarity index 97% rename from resource/ep_test.go rename to internal/resource/ep_test.go index 1478a070..62f815a4 100644 --- a/resource/ep_test.go +++ b/internal/resource/ep_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/evt.go b/internal/resource/evt.go similarity index 98% rename from resource/evt.go rename to internal/resource/evt.go index 255bd703..4062df0b 100644 --- a/resource/evt.go +++ b/internal/resource/evt.go @@ -4,7 +4,7 @@ import ( "regexp" "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/evt_test.go b/internal/resource/evt_test.go similarity index 97% rename from resource/evt_test.go rename to internal/resource/evt_test.go index 027b5b87..ee96b87d 100644 --- a/resource/evt_test.go +++ b/internal/resource/evt_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/helpers.go b/internal/resource/helpers.go similarity index 100% rename from resource/helpers.go rename to internal/resource/helpers.go diff --git a/resource/helpers_test.go b/internal/resource/helpers_test.go similarity index 100% rename from resource/helpers_test.go rename to internal/resource/helpers_test.go diff --git a/resource/hpa.go b/internal/resource/hpa.go similarity index 98% rename from resource/hpa.go rename to internal/resource/hpa.go index 0e0ceff1..038ec139 100644 --- a/resource/hpa.go +++ b/internal/resource/hpa.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/autoscaling/v1" diff --git a/resource/hpa_test.go b/internal/resource/hpa_test.go similarity index 97% rename from resource/hpa_test.go rename to internal/resource/hpa_test.go index 71835c73..cad535b9 100644 --- a/resource/hpa_test.go +++ b/internal/resource/hpa_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/autoscaling/v1" diff --git a/resource/ing.go b/internal/resource/ing.go similarity index 98% rename from resource/ing.go rename to internal/resource/ing.go index 9fb6e832..71741163 100644 --- a/resource/ing.go +++ b/internal/resource/ing.go @@ -3,7 +3,7 @@ package resource import ( "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/ing_test.go b/internal/resource/ing_test.go similarity index 97% rename from resource/ing_test.go rename to internal/resource/ing_test.go index adb94a6f..f8785f18 100644 --- a/resource/ing_test.go +++ b/internal/resource/ing_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/extensions/v1beta1" diff --git a/resource/job.go b/internal/resource/job.go similarity index 98% rename from resource/job.go rename to internal/resource/job.go index d8b78be8..9585ea93 100644 --- a/resource/job.go +++ b/internal/resource/job.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/batch/v1" diff --git a/resource/job_test.go b/internal/resource/job_test.go similarity index 98% rename from resource/job_test.go rename to internal/resource/job_test.go index 6a1309d1..23648862 100644 --- a/resource/job_test.go +++ b/internal/resource/job_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/batch/v1" diff --git a/resource/list.go b/internal/resource/list.go similarity index 99% rename from resource/list.go rename to internal/resource/list.go index b66bef44..e3e749e9 100644 --- a/resource/list.go +++ b/internal/resource/list.go @@ -4,7 +4,7 @@ import ( "reflect" "sort" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/watch" ) diff --git a/resource/mock_caller_test.go b/internal/resource/mock_caller_test.go similarity index 98% rename from resource/mock_caller_test.go rename to internal/resource/mock_caller_test.go index 29562d4c..9cea7321 100644 --- a/resource/mock_caller_test.go +++ b/internal/resource/mock_caller_test.go @@ -1,10 +1,10 @@ // Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/resource (interfaces: Caller) +// Source: github.com/derailed/k9s/internal/resource (interfaces: Caller) package resource_test import ( - k8s "github.com/derailed/k9s/resource/k8s" + k8s "github.com/derailed/k9s/internal/k8s" pegomock "github.com/petergtz/pegomock" "reflect" "time" diff --git a/resource/mock_clusterifc_test.go b/internal/resource/mock_clusterifc_test.go similarity index 62% rename from resource/mock_clusterifc_test.go rename to internal/resource/mock_clusterifc_test.go index 7be9872b..88ac1f26 100644 --- a/resource/mock_clusterifc_test.go +++ b/internal/resource/mock_clusterifc_test.go @@ -1,5 +1,5 @@ // Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/resource (interfaces: ClusterIfc) +// Source: github.com/derailed/k9s/internal/resource (interfaces: ClusterIfc) package resource_test @@ -32,6 +32,36 @@ func (mock *MockClusterIfc) ClusterName() string { return ret0 } +func (mock *MockClusterIfc) ContextName() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockClusterIfc().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("ContextName", 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 *MockClusterIfc) UserName() string { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockClusterIfc().") + } + params := []pegomock.Param{} + result := pegomock.GetGenericMockFrom(mock).Invoke("UserName", 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 *MockClusterIfc) Version() (string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClusterIfc().") @@ -105,6 +135,40 @@ func (c *ClusterIfc_ClusterName_OngoingVerification) GetCapturedArguments() { func (c *ClusterIfc_ClusterName_OngoingVerification) GetAllCapturedArguments() { } +func (verifier *VerifierClusterIfc) ContextName() *ClusterIfc_ContextName_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ContextName", params, verifier.timeout) + return &ClusterIfc_ContextName_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type ClusterIfc_ContextName_OngoingVerification struct { + mock *MockClusterIfc + methodInvocations []pegomock.MethodInvocation +} + +func (c *ClusterIfc_ContextName_OngoingVerification) GetCapturedArguments() { +} + +func (c *ClusterIfc_ContextName_OngoingVerification) GetAllCapturedArguments() { +} + +func (verifier *VerifierClusterIfc) UserName() *ClusterIfc_UserName_OngoingVerification { + params := []pegomock.Param{} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UserName", params, verifier.timeout) + return &ClusterIfc_UserName_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type ClusterIfc_UserName_OngoingVerification struct { + mock *MockClusterIfc + methodInvocations []pegomock.MethodInvocation +} + +func (c *ClusterIfc_UserName_OngoingVerification) GetCapturedArguments() { +} + +func (c *ClusterIfc_UserName_OngoingVerification) GetAllCapturedArguments() { +} + func (verifier *VerifierClusterIfc) Version() *ClusterIfc_Version_OngoingVerification { params := []pegomock.Param{} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Version", params, verifier.timeout) diff --git a/resource/mock_metricsifc_test.go b/internal/resource/mock_metricsifc_test.go similarity index 97% rename from resource/mock_metricsifc_test.go rename to internal/resource/mock_metricsifc_test.go index 6a97b05d..666ba4b4 100644 --- a/resource/mock_metricsifc_test.go +++ b/internal/resource/mock_metricsifc_test.go @@ -1,10 +1,10 @@ // Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/resource (interfaces: MetricsIfc) +// Source: github.com/derailed/k9s/internal/resource (interfaces: MetricsIfc) package resource_test import ( - k8s "github.com/derailed/k9s/resource/k8s" + k8s "github.com/derailed/k9s/internal/k8s" pegomock "github.com/petergtz/pegomock" v1 "k8s.io/api/core/v1" "reflect" diff --git a/resource/mock_resource_test.go b/internal/resource/mock_resource_test.go similarity index 98% rename from resource/mock_resource_test.go rename to internal/resource/mock_resource_test.go index 48ebb1b2..60fdc29e 100644 --- a/resource/mock_resource_test.go +++ b/internal/resource/mock_resource_test.go @@ -1,10 +1,10 @@ // Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/resource (interfaces: Resource) +// Source: github.com/derailed/k9s/internal/resource (interfaces: Resource) package resource_test import ( - resource "github.com/derailed/k9s/resource" + resource "github.com/derailed/k9s/internal/resource" pegomock "github.com/petergtz/pegomock" "reflect" "time" diff --git a/resource/mock_switchableres_test.go b/internal/resource/mock_switchableres_test.go similarity index 98% rename from resource/mock_switchableres_test.go rename to internal/resource/mock_switchableres_test.go index 74146fa6..4585b31e 100644 --- a/resource/mock_switchableres_test.go +++ b/internal/resource/mock_switchableres_test.go @@ -1,10 +1,10 @@ // Code generated by pegomock. DO NOT EDIT. -// Source: github.com/derailed/k9s/resource (interfaces: SwitchableRes) +// Source: github.com/derailed/k9s/internal/resource (interfaces: SwitchableRes) package resource_test import ( - k8s "github.com/derailed/k9s/resource/k8s" + k8s "github.com/derailed/k9s/internal/k8s" pegomock "github.com/petergtz/pegomock" "reflect" "time" diff --git a/resource/no.go b/internal/resource/no.go similarity index 99% rename from resource/no.go rename to internal/resource/no.go index 55cd927b..1faf9a97 100644 --- a/resource/no.go +++ b/internal/resource/no.go @@ -5,7 +5,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/no_test.go b/internal/resource/no_test.go similarity index 97% rename from resource/no_test.go rename to internal/resource/no_test.go index 277d0848..fb5b41cf 100644 --- a/resource/no_test.go +++ b/internal/resource/no_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/ns.go b/internal/resource/ns.go similarity index 98% rename from resource/ns.go rename to internal/resource/ns.go index a0ab86fb..ea9cd3c8 100644 --- a/resource/ns.go +++ b/internal/resource/ns.go @@ -1,7 +1,7 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/ns_test.go b/internal/resource/ns_test.go similarity index 97% rename from resource/ns_test.go rename to internal/resource/ns_test.go index e09f339c..56e8d5cc 100644 --- a/resource/ns_test.go +++ b/internal/resource/ns_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/pod.go b/internal/resource/pod.go similarity index 99% rename from resource/pod.go rename to internal/resource/pod.go index de1059b7..ab71c4e5 100644 --- a/resource/pod.go +++ b/internal/resource/pod.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/pod_test.go b/internal/resource/pod_test.go similarity index 98% rename from resource/pod_test.go rename to internal/resource/pod_test.go index f61777a6..726e29f9 100644 --- a/resource/pod_test.go +++ b/internal/resource/pod_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/pv.go b/internal/resource/pv.go similarity index 98% rename from resource/pv.go rename to internal/resource/pv.go index 605ac06a..2c2d792b 100644 --- a/resource/pv.go +++ b/internal/resource/pv.go @@ -4,7 +4,7 @@ import ( "path" "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/pv_test.go b/internal/resource/pv_test.go similarity index 97% rename from resource/pv_test.go rename to internal/resource/pv_test.go index 5bbd1f22..3d5d9b53 100644 --- a/resource/pv_test.go +++ b/internal/resource/pv_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/pvc.go b/internal/resource/pvc.go similarity index 98% rename from resource/pvc.go rename to internal/resource/pvc.go index 180f3f37..f10aaafb 100644 --- a/resource/pvc.go +++ b/internal/resource/pvc.go @@ -1,7 +1,7 @@ package resource import ( - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/pvc_test.go b/internal/resource/pvc_test.go similarity index 97% rename from resource/pvc_test.go rename to internal/resource/pvc_test.go index 85dee441..79ea837b 100644 --- a/resource/pvc_test.go +++ b/internal/resource/pvc_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/rc.go b/internal/resource/rc.go similarity index 98% rename from resource/rc.go rename to internal/resource/rc.go index 1917ba67..ca39f310 100644 --- a/resource/rc.go +++ b/internal/resource/rc.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/ro.go b/internal/resource/ro.go similarity index 98% rename from resource/ro.go rename to internal/resource/ro.go index 36284c94..06dc52f1 100644 --- a/resource/ro.go +++ b/internal/resource/ro.go @@ -3,7 +3,7 @@ package resource import ( "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/rbac/v1" diff --git a/resource/ro_binding.go b/internal/resource/ro_binding.go similarity index 98% rename from resource/ro_binding.go rename to internal/resource/ro_binding.go index f37b2fff..d5ad3e15 100644 --- a/resource/ro_binding.go +++ b/internal/resource/ro_binding.go @@ -3,7 +3,7 @@ package resource import ( "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/rbac/v1" diff --git a/resource/ro_binding_int_test.go b/internal/resource/ro_binding_int_test.go similarity index 100% rename from resource/ro_binding_int_test.go rename to internal/resource/ro_binding_int_test.go diff --git a/resource/ro_binding_test.go b/internal/resource/ro_binding_test.go similarity index 96% rename from resource/ro_binding_test.go rename to internal/resource/ro_binding_test.go index ebad496f..da8bd1bb 100644 --- a/resource/ro_binding_test.go +++ b/internal/resource/ro_binding_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/rbac/v1" diff --git a/resource/ro_test.go b/internal/resource/ro_test.go similarity index 96% rename from resource/ro_test.go rename to internal/resource/ro_test.go index 6e15977a..8d48b1f9 100644 --- a/resource/ro_test.go +++ b/internal/resource/ro_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/rbac/v1" diff --git a/resource/rs.go b/internal/resource/rs.go similarity index 98% rename from resource/rs.go rename to internal/resource/rs.go index 3ec29c06..b4b262e8 100644 --- a/resource/rs.go +++ b/internal/resource/rs.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/apps/v1" diff --git a/resource/rs_test.go b/internal/resource/rs_test.go similarity index 97% rename from resource/rs_test.go rename to internal/resource/rs_test.go index b46bc19d..cd1fa7b8 100644 --- a/resource/rs_test.go +++ b/internal/resource/rs_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/apps/v1" diff --git a/resource/sa.go b/internal/resource/sa.go similarity index 98% rename from resource/sa.go rename to internal/resource/sa.go index d09c71b6..34d73109 100644 --- a/resource/sa.go +++ b/internal/resource/sa.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "k8s.io/api/core/v1" diff --git a/resource/sa_test.go b/internal/resource/sa_test.go similarity index 97% rename from resource/sa_test.go rename to internal/resource/sa_test.go index de8fdca5..6874ee99 100644 --- a/resource/sa_test.go +++ b/internal/resource/sa_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/secret.go b/internal/resource/secret.go similarity index 98% rename from resource/secret.go rename to internal/resource/secret.go index 89344759..c83eb8f9 100644 --- a/resource/secret.go +++ b/internal/resource/secret.go @@ -4,7 +4,7 @@ import ( "log" "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" yaml "gopkg.in/yaml.v2" "k8s.io/api/core/v1" ) diff --git a/resource/secret_test.go b/internal/resource/secret_test.go similarity index 98% rename from resource/secret_test.go rename to internal/resource/secret_test.go index 775b1878..b4253287 100644 --- a/resource/secret_test.go +++ b/internal/resource/secret_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" diff --git a/resource/sts.go b/internal/resource/sts.go similarity index 98% rename from resource/sts.go rename to internal/resource/sts.go index dbaa374f..0236f68f 100644 --- a/resource/sts.go +++ b/internal/resource/sts.go @@ -3,7 +3,7 @@ package resource import ( "strconv" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" "k8s.io/api/apps/v1" diff --git a/resource/sts_test.go b/internal/resource/sts_test.go similarity index 98% rename from resource/sts_test.go rename to internal/resource/sts_test.go index 4cf12ca6..d7441177 100644 --- a/resource/sts_test.go +++ b/internal/resource/sts_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" "k8s.io/api/apps/v1" diff --git a/resource/svc.go b/internal/resource/svc.go similarity index 99% rename from resource/svc.go rename to internal/resource/svc.go index 20441b00..6b2b34ac 100644 --- a/resource/svc.go +++ b/internal/resource/svc.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/k8s" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" diff --git a/resource/svc_int_test.go b/internal/resource/svc_int_test.go similarity index 100% rename from resource/svc_int_test.go rename to internal/resource/svc_int_test.go diff --git a/resource/svc_test.go b/internal/resource/svc_test.go similarity index 98% rename from resource/svc_test.go rename to internal/resource/svc_test.go index 2b943a8b..b906e3af 100644 --- a/resource/svc_test.go +++ b/internal/resource/svc_test.go @@ -3,8 +3,8 @@ package resource_test import ( "testing" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" m "github.com/petergtz/pegomock" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" diff --git a/views/app.go b/internal/views/app.go similarity index 79% rename from views/app.go rename to internal/views/app.go index 1b15b285..a5fb4ea3 100644 --- a/views/app.go +++ b/internal/views/app.go @@ -6,9 +6,7 @@ import ( "os" "time" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource/k8s" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/config" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" @@ -70,11 +68,6 @@ func NewApp() *appView { func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags) { a.version = v - - log.Info("🐶 K9s starting up...") - mustK8s() - initConfig(rate, flags) - a.infoView = newInfoView(a) a.infoView.init() @@ -93,7 +86,7 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags) main := tview.NewFlex() { main.SetDirection(tview.FlexRow) - main.AddItem(header, 6, 1, false) + main.AddItem(header, 7, 1, false) main.AddItem(a.cmdView, 1, 1, false) main.AddItem(a.content, 0, 10, true) main.AddItem(a.flashView, 2, 1, false) @@ -104,41 +97,6 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags) a.SetRoot(a.pages, true) } -func initConfig(rate int, flags *genericclioptions.ConfigFlags) { - if err := config.Load(config.K9sConfigFile); err != nil { - log.Errorf("Failed to load K9s config. Reset to default config!") - } - config.Root.Validate(k8s.ClusterInfo{}) - - config.Root.K9s.RefreshRate = rate - - activeCluster := k8s.ActiveClusterOrDie() - if flags.ClusterName != nil && len(*flags.ClusterName) != 0 { - config.Root.SetActiveCluster(*flags.ClusterName) - var ctx k8s.Context - ctx.Switch(config.Root.ActiveClusterName()) - } - - if config.Root.ActiveClusterName() != activeCluster { - config.Root.SetActiveCluster(activeCluster) - } - - if flags.Namespace != nil && len(*flags.Namespace) != 0 { - cluster := config.Root.K9s.Context.Clusters[activeCluster] - cluster.Namespace.Active = *flags.Namespace - } - - config.Root.Save(k8s.ClusterInfo{}) -} - -func mustK8s() { - k8s.ConfigOrDie() - if _, err := k8s.NewNamespace().List(resource.DefaultNamespace); err != nil { - panic(err) - } - log.Info("Kubernetes connectivity ✅") -} - // Run starts the application loop func (a *appView) Run() { go func() { @@ -167,7 +125,7 @@ func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey { return evt } case a.cmdBuff.hotKey: - a.flash(flashInfo,"Entering command mode...") + a.flash(flashInfo, "Entering command mode...") log.Debug("K9s entering command mode...") a.cmdBuff.setActive(true) a.cmdBuff.clear() diff --git a/views/cmd.go b/internal/views/cmd.go similarity index 100% rename from views/cmd.go rename to internal/views/cmd.go diff --git a/views/cmd_buff.go b/internal/views/cmd_buff.go similarity index 100% rename from views/cmd_buff.go rename to internal/views/cmd_buff.go diff --git a/views/cmd_buff_test.go b/internal/views/cmd_buff_test.go similarity index 100% rename from views/cmd_buff_test.go rename to internal/views/cmd_buff_test.go diff --git a/views/colorer.go b/internal/views/colorer.go similarity index 98% rename from views/colorer.go rename to internal/views/colorer.go index f72f726d..8846c446 100644 --- a/views/colorer.go +++ b/internal/views/colorer.go @@ -3,7 +3,7 @@ package views import ( "strings" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "k8s.io/apimachinery/pkg/watch" ) diff --git a/views/colorer_test.go b/internal/views/colorer_test.go similarity index 99% rename from views/colorer_test.go rename to internal/views/colorer_test.go index 9130a6c9..7c5f88ff 100644 --- a/views/colorer_test.go +++ b/internal/views/colorer_test.go @@ -3,7 +3,7 @@ package views import ( "testing" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/watch" diff --git a/views/command.go b/internal/views/command.go similarity index 88% rename from views/command.go rename to internal/views/command.go index 0cb76db4..bedde239 100644 --- a/views/command.go +++ b/internal/views/command.go @@ -3,9 +3,8 @@ package views import ( "fmt" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/resource" ) type command struct { @@ -37,8 +36,8 @@ func (c *command) run(cmd string) { c.app.flash(flashInfo, "Viewing all "+res.title+"...") c.exec(cmd, v) return + } } -} res, ok := getCRDS()[cmd] if !ok { @@ -62,7 +61,7 @@ func (c *command) run(cmd string) { func (c *command) exec(cmd string, v igniter) { if v != nil { config.Root.SetActiveView(cmd) - config.Root.Save(k8s.ClusterInfo{}) + config.Root.Save() c.app.inject(v) } } diff --git a/views/context.go b/internal/views/context.go similarity index 84% rename from views/context.go rename to internal/views/context.go index fe77264e..92a7116b 100644 --- a/views/context.go +++ b/internal/views/context.go @@ -3,9 +3,8 @@ package views import ( "strings" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" - "github.com/derailed/k9s/config" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" ) @@ -41,8 +40,8 @@ func (v *contextView) useContext(*tcell.EventKey) { return } - config.Root.SetActiveCluster(ctx) - config.Root.Save(k8s.ClusterInfo{}) + config.Root.Reset() + config.Root.Save() v.app.flash(flashInfo, "Switching context to", ctx) v.refresh() diff --git a/views/details.go b/internal/views/details.go similarity index 100% rename from views/details.go rename to internal/views/details.go diff --git a/views/exec.go b/internal/views/exec.go similarity index 100% rename from views/exec.go rename to internal/views/exec.go diff --git a/views/flash.go b/internal/views/flash.go similarity index 100% rename from views/flash.go rename to internal/views/flash.go diff --git a/views/flash_test.go b/internal/views/flash_test.go similarity index 100% rename from views/flash_test.go rename to internal/views/flash_test.go diff --git a/views/help.go b/internal/views/help.go similarity index 97% rename from views/help.go rename to internal/views/help.go index 0d9ccc21..c8a26910 100644 --- a/views/help.go +++ b/internal/views/help.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" ) diff --git a/views/helpers.go b/internal/views/helpers.go similarity index 100% rename from views/helpers.go rename to internal/views/helpers.go diff --git a/views/helpers_test.go b/internal/views/helpers_test.go similarity index 94% rename from views/helpers_test.go rename to internal/views/helpers_test.go index 31811cd9..1c2a716c 100644 --- a/views/helpers_test.go +++ b/internal/views/helpers_test.go @@ -3,7 +3,7 @@ package views import ( "testing" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/stretchr/testify/assert" ) diff --git a/views/info.go b/internal/views/info.go similarity index 72% rename from views/info.go rename to internal/views/info.go index 90594b50..658b9992 100644 --- a/views/info.go +++ b/internal/views/info.go @@ -1,7 +1,7 @@ package views import ( - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" @@ -21,8 +21,17 @@ func (v *infoView) init() { var row int cluster := resource.NewCluster() + + v.SetCell(row, 0, v.sectionCell("Context")) + v.SetCell(row, 1, v.infoCell(cluster.ContextName())) + row++ + v.SetCell(row, 0, v.sectionCell("Cluster")) - v.SetCell(row, 1, v.infoCell(cluster.Name())) + v.SetCell(row, 1, v.infoCell(cluster.ClusterName())) + row++ + + v.SetCell(row, 0, v.sectionCell("User")) + v.SetCell(row, 1, v.infoCell(cluster.UserName())) row++ v.SetCell(row, 0, v.sectionCell("K9s Version")) @@ -58,10 +67,13 @@ func (v *infoView) refresh() { var row int cluster := resource.NewCluster() - v.GetCell(row, 1).SetText(cluster.Name()) - row+=2 - rev := cluster.Version() - v.GetCell(row, 1).SetText(rev) + v.GetCell(row, 1).SetText(cluster.ContextName()) + row++ + v.GetCell(row, 1).SetText(cluster.ClusterName()) + row++ + v.GetCell(row, 1).SetText(cluster.UserName()) + row += 2 + v.GetCell(row, 1).SetText(cluster.Version()) row++ mx, err := cluster.Metrics() diff --git a/views/log.go b/internal/views/log.go similarity index 100% rename from views/log.go rename to internal/views/log.go diff --git a/views/logs.go b/internal/views/logs.go similarity index 98% rename from views/logs.go rename to internal/views/logs.go index 953bd551..82949934 100644 --- a/views/logs.go +++ b/internal/views/logs.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" diff --git a/views/menu.go b/internal/views/menu.go similarity index 96% rename from views/menu.go rename to internal/views/menu.go index 66f6125e..0a12c43d 100644 --- a/views/menu.go +++ b/internal/views/menu.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" ) @@ -108,7 +108,7 @@ func (v *menuView) item(h hint) string { if len(h.mnemonic) == 1 { s = fmt.Sprintf(menuSepFmt, strings.ToLower(h.mnemonic), h.display) } else { - s = fmt.Sprintf(menuSepFmt, strings.ToUpper(h.mnemonic), h.display) + s = fmt.Sprintf(menuSepFmt, strings.ToUpper(h.mnemonic), h.display) } return s } diff --git a/views/namespace.go b/internal/views/namespace.go similarity index 91% rename from views/namespace.go rename to internal/views/namespace.go index fecaf4cd..04aebac4 100644 --- a/views/namespace.go +++ b/internal/views/namespace.go @@ -4,9 +4,8 @@ import ( "fmt" "strings" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" ) @@ -44,7 +43,7 @@ func (v *namespaceView) useNamespace(*tcell.EventKey) { v.refresh() config.Root.SetActiveNamespace(ns) - config.Root.Save(k8s.ClusterInfo{}) + config.Root.Save() v.app.flash(flashInfo, fmt.Sprintf("Setting namespace `%s as your default namespace", ns)) } diff --git a/views/pod.go b/internal/views/pod.go similarity index 97% rename from views/pod.go rename to internal/views/pod.go index 893ad2c3..caf37482 100644 --- a/views/pod.go +++ b/internal/views/pod.go @@ -1,7 +1,7 @@ package views import ( - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" log "github.com/sirupsen/logrus" ) diff --git a/views/registrar.go b/internal/views/registrar.go similarity index 96% rename from views/registrar.go rename to internal/views/registrar.go index 2e925815..f6d14477 100644 --- a/views/registrar.go +++ b/internal/views/registrar.go @@ -1,8 +1,8 @@ package views import ( - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/k8s" "github.com/gdamore/tcell" ) @@ -216,13 +216,13 @@ func helpCmds() map[string]resCmd { return cmds } -func getCRDS() map[string]k8s.ApiGroup { - m := map[string]k8s.ApiGroup{} +func getCRDS() map[string]k8s.APIGroup { + m := map[string]k8s.APIGroup{} list := resource.NewCRDList(resource.AllNamespaces) ll, _ := list.Resource().List(resource.AllNamespaces) for _, l := range ll { ff := l.ExtFields() - grp := k8s.ApiGroup{ + grp := k8s.APIGroup{ Version: ff["version"].(string), Group: ff["group"].(string), Kind: ff["kind"].(string), diff --git a/views/resource.go b/internal/views/resource.go similarity index 98% rename from views/resource.go rename to internal/views/resource.go index 41eeaf32..76930ae3 100644 --- a/views/resource.go +++ b/internal/views/resource.go @@ -10,9 +10,9 @@ import ( "sync" "time" - "github.com/derailed/k9s/config" - "github.com/derailed/k9s/resource" - "github.com/derailed/k9s/resource/k8s" + "github.com/derailed/k9s/internal/config" + "github.com/derailed/k9s/internal/k8s" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" @@ -215,7 +215,7 @@ func (v *resourceView) doSwitchNamespace(ns string) { v.getTV().table.Select(0, 0) v.app.cmdBuff.reset() config.Root.SetActiveNamespace(v.selectedNS) - config.Root.Save(k8s.ClusterInfo{}) + config.Root.Save() } // Utils... diff --git a/views/select_list.go b/internal/views/select_list.go similarity index 91% rename from views/select_list.go rename to internal/views/select_list.go index bdbdf925..a2659fce 100644 --- a/views/select_list.go +++ b/internal/views/select_list.go @@ -5,7 +5,6 @@ import ( "github.com/gdamore/tcell" "github.com/k8sland/tview" - log "github.com/sirupsen/logrus" ) type selectList struct { @@ -34,7 +33,6 @@ func (v *selectList) setActions(aa keyActions) { } func (v *selectList) hints() hints { - log.Println("SelectList got hints!!") if v.actions != nil { return v.actions.toHints() } diff --git a/views/splash.go b/internal/views/splash.go similarity index 100% rename from views/splash.go rename to internal/views/splash.go diff --git a/views/table.go b/internal/views/table.go similarity index 99% rename from views/table.go rename to internal/views/table.go index 0fc7a40a..2042b14a 100644 --- a/views/table.go +++ b/internal/views/table.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" log "github.com/sirupsen/logrus" diff --git a/views/utils.go b/internal/views/utils.go similarity index 100% rename from views/utils.go rename to internal/views/utils.go diff --git a/views/utils_test.go b/internal/views/utils_test.go similarity index 100% rename from views/utils_test.go rename to internal/views/utils_test.go diff --git a/views/xray.go b/internal/views/xray.go similarity index 97% rename from views/xray.go rename to internal/views/xray.go index d1ea009e..c2e44bdc 100644 --- a/views/xray.go +++ b/internal/views/xray.go @@ -3,7 +3,7 @@ package views import ( "context" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" ) diff --git a/views/yaml.go b/internal/views/yaml.go similarity index 98% rename from views/yaml.go rename to internal/views/yaml.go index d301642c..a95b3032 100644 --- a/views/yaml.go +++ b/internal/views/yaml.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/derailed/k9s/resource" + "github.com/derailed/k9s/internal/resource" "github.com/gdamore/tcell" "github.com/k8sland/tview" ) diff --git a/main.go b/main.go index 2453d951..c4979215 100644 --- a/main.go +++ b/main.go @@ -3,14 +3,16 @@ package main import ( "os" - "github.com/derailed/k9s/cmd" - "github.com/derailed/k9s/config" + "github.com/derailed/k9s/internal/cmd" + "github.com/derailed/k9s/internal/config" log "github.com/sirupsen/logrus" ) func init() { + config.EnsurePath(config.K9sLogs, config.DefaultDirMod) + mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY - if file, err := os.OpenFile(config.K9sLogs, mod, 0644); err == nil { + if file, err := os.OpenFile(config.K9sLogs, mod, config.DefaultFileMod); err == nil { log.SetOutput(file) } else { panic(err) diff --git a/resource/k8s/cluster.go b/resource/k8s/cluster.go deleted file mode 100644 index f4dc68c8..00000000 --- a/resource/k8s/cluster.go +++ /dev/null @@ -1,23 +0,0 @@ -package k8s - -// Cluster manages a Kubernetes ClusterRole. -type Cluster struct{} - -// NewCluster instantiates a new ClusterRole. -func NewCluster() *Cluster { - return &Cluster{} -} - -// Version retrieves cluster git version. -func (c *Cluster) Version() (string, error) { - rev, err := conn.dialOrDie().Discovery().ServerVersion() - if err != nil { - return "", err - } - return rev.GitVersion, nil -} - -// ClusterName retrieves cluster name. -func (c *Cluster) ClusterName() string { - return conn.getClusterName() -} diff --git a/resource/k8s/cluster_info.go b/resource/k8s/cluster_info.go deleted file mode 100644 index 1aa00fe8..00000000 --- a/resource/k8s/cluster_info.go +++ /dev/null @@ -1,15 +0,0 @@ -package k8s - -type ClusterInfo struct{} - -func (ClusterInfo) ActiveClusterOrDie() string { - return ActiveClusterOrDie() -} - -func (ClusterInfo) AllClustersOrDie() []string { - return AllClustersOrDie() -} - -func (ClusterInfo) AllNamespacesOrDie() []string { - return AllNamespacesOrDie() -} \ No newline at end of file