refactor command line args + beefup tests
parent
52e817bff2
commit
b358f3631f
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
`
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
1
go.mod
1
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
|
||||
|
|
|
|||
24
go.sum
24
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=
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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"`
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
|
@ -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
|
||||
`
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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==
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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{}
|
||||
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 {
|
||||
|
|
@ -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==
|
||||
|
|
@ -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==
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
|
|
@ -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 += "*"
|
||||
}
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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),
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue