beefup tests
parent
d71411a788
commit
1562b6255e
|
|
@ -16,6 +16,7 @@ type (
|
|||
ContextName() string
|
||||
ClusterName() string
|
||||
UserName() string
|
||||
FetchNodes() ([]v1.Node, error)
|
||||
}
|
||||
|
||||
// MetricsServer gather metrics information from pods and nodes.
|
||||
|
|
@ -42,8 +43,8 @@ type (
|
|||
)
|
||||
|
||||
// NewCluster returns a new cluster info resource.
|
||||
func NewCluster(c Connection, log *zerolog.Logger) *Cluster {
|
||||
return NewClusterWithArgs(k8s.NewCluster(c, log), k8s.NewMetricsServer(c))
|
||||
func NewCluster(c Connection, log *zerolog.Logger, mx MetricsServer) *Cluster {
|
||||
return NewClusterWithArgs(k8s.NewCluster(c, log), mx)
|
||||
}
|
||||
|
||||
// NewClusterWithArgs for tests only!
|
||||
|
|
@ -81,21 +82,12 @@ func (c *Cluster) Metrics(nodes []v1.Node, nmx []mv1beta1.NodeMetrics) k8s.Clust
|
|||
return c.mx.ClusterLoad(nodes, nmx)
|
||||
}
|
||||
|
||||
// GetNodesMetrics fetch all nodes metrics.
|
||||
func (c *Cluster) GetNodesMetrics() ([]mv1beta1.NodeMetrics, error) {
|
||||
// FetchNodesMetrics fetch all nodes metrics.
|
||||
func (c *Cluster) FetchNodesMetrics() ([]mv1beta1.NodeMetrics, error) {
|
||||
return c.mx.FetchNodesMetrics()
|
||||
}
|
||||
|
||||
// GetNodes fetch all available nodes.
|
||||
func (c *Cluster) GetNodes() ([]v1.Node, error) {
|
||||
nn, err := k8s.NewNode(c.api).List("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes := make([]v1.Node, 0, len(nn))
|
||||
for _, n := range nn {
|
||||
nodes = append(nodes, n.(v1.Node))
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
// FetchNodes fetch all available nodes.
|
||||
func (c *Cluster) FetchNodes() ([]v1.Node, error) {
|
||||
return c.api.FetchNodes()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,37 +13,77 @@ import (
|
|||
)
|
||||
|
||||
func TestClusterVersion(t *testing.T) {
|
||||
cIfc, mxIfc := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(cIfc.Version()).ThenReturn("1.2.3", nil)
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.Version()).ThenReturn("1.2.3", nil)
|
||||
|
||||
ci := resource.NewClusterWithArgs(cIfc, mxIfc)
|
||||
ci := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, "1.2.3", ci.Version())
|
||||
}
|
||||
|
||||
func TestClusterNoVersion(t *testing.T) {
|
||||
cIfc, mxIfc := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(cIfc.Version()).ThenReturn("bad", fmt.Errorf("No data"))
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.Version()).ThenReturn("bad", fmt.Errorf("No data"))
|
||||
|
||||
ci := resource.NewClusterWithArgs(cIfc, mxIfc)
|
||||
ci := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, "n/a", ci.Version())
|
||||
}
|
||||
|
||||
func TestClusterName(t *testing.T) {
|
||||
cIfc, mxIfc := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(cIfc.ClusterName()).ThenReturn("fred")
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.ClusterName()).ThenReturn("fred")
|
||||
|
||||
ci := resource.NewClusterWithArgs(cIfc, mxIfc)
|
||||
ci := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, "fred", ci.ClusterName())
|
||||
}
|
||||
|
||||
func TestClusterMetrics(t *testing.T) {
|
||||
cIfc, mxIfc := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mxIfc.ClusterLoad([]v1.Node{}, []mv1beta1.NodeMetrics{})).ThenReturn(clusterMetric())
|
||||
func TestContextName(t *testing.T) {
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.ContextName()).ThenReturn("fred")
|
||||
|
||||
c := resource.NewClusterWithArgs(cIfc, mxIfc)
|
||||
ci := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, "fred", ci.ContextName())
|
||||
}
|
||||
|
||||
func TestUserName(t *testing.T) {
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.UserName()).ThenReturn("fred")
|
||||
|
||||
ci := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, "fred", ci.UserName())
|
||||
}
|
||||
|
||||
func TestClusterMetrics(t *testing.T) {
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mx.ClusterLoad([]v1.Node{}, []mv1beta1.NodeMetrics{})).ThenReturn(clusterMetric())
|
||||
|
||||
c := resource.NewClusterWithArgs(mm, mx)
|
||||
assert.Equal(t, clusterMetric(), c.Metrics([]v1.Node{}, []mv1beta1.NodeMetrics{}))
|
||||
}
|
||||
|
||||
func TestClusterGetNodes(t *testing.T) {
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.FetchNodes()).ThenReturn([]v1.Node{*k8sNode()}, nil)
|
||||
m.When(mx.ClusterLoad([]v1.Node{}, []mv1beta1.NodeMetrics{})).ThenReturn(clusterMetric())
|
||||
|
||||
c := resource.NewClusterWithArgs(mm, mx)
|
||||
nodes, err := c.FetchNodes()
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(nodes))
|
||||
}
|
||||
|
||||
func TestClusterFetchNodesMetrics(t *testing.T) {
|
||||
mm, mx := NewMockClusterMeta(), NewMockMetricsServer()
|
||||
m.When(mm.FetchNodes()).ThenReturn([]v1.Node{*k8sNode()}, nil)
|
||||
m.When(mx.FetchNodesMetrics()).ThenReturn([]mv1beta1.NodeMetrics{makeMxNode("fred", "100m", "10Mi")}, nil)
|
||||
|
||||
c := resource.NewClusterWithArgs(mm, mx)
|
||||
metrics, err := c.FetchNodesMetrics()
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(metrics))
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
||||
func TestUsingMocks(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -5,25 +5,27 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// SwitchableResource represents a resource that can be switched.
|
||||
type SwitchableResource interface {
|
||||
k8s.Cruder
|
||||
k8s.Switchable
|
||||
}
|
||||
type (
|
||||
// SwitchableResource represents a resource that can be switched.
|
||||
SwitchableResource interface {
|
||||
Cruder
|
||||
k8s.Switchable
|
||||
}
|
||||
|
||||
// Context tracks a kubernetes resource.
|
||||
type Context struct {
|
||||
*Base
|
||||
instance *k8s.NamedContext
|
||||
}
|
||||
// Context tracks a kubernetes resource.
|
||||
Context struct {
|
||||
*Base
|
||||
instance *k8s.NamedContext
|
||||
}
|
||||
)
|
||||
|
||||
// NewContextList returns a new resource list.
|
||||
func NewContextList(c k8s.Connection, ns string) List {
|
||||
func NewContextList(c Connection, ns string) List {
|
||||
return NewList(NotNamespaced, "ctx", NewContext(c), SwitchAccess)
|
||||
}
|
||||
|
||||
// NewContext instantiates a new Context.
|
||||
func NewContext(c k8s.Connection) *Context {
|
||||
func NewContext(c Connection) *Context {
|
||||
ctx := &Context{Base: NewBase(c, k8s.NewContext(c))}
|
||||
ctx.Factory = ctx
|
||||
|
||||
|
|
@ -64,15 +66,14 @@ func (*Context) Header(string) Row {
|
|||
// Fields retrieves displayable fields.
|
||||
func (r *Context) Fields(ns string) Row {
|
||||
ff := make(Row, 0, len(r.Header(ns)))
|
||||
i := r.instance
|
||||
|
||||
name := i.Name
|
||||
if i.MustCurrentContextName() == name {
|
||||
name += "*"
|
||||
i := r.instance
|
||||
if i.MustCurrentContextName() == i.Name {
|
||||
i.Name += "*"
|
||||
}
|
||||
|
||||
return append(ff,
|
||||
name,
|
||||
i.Name,
|
||||
i.Context.Cluster,
|
||||
i.Context.AuthInfo,
|
||||
i.Context.Namespace,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/resource"
|
||||
m "github.com/petergtz/pegomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
api "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
|
|
@ -21,73 +22,103 @@ func NewContextWithArgs(c k8s.Connection, s resource.SwitchableResource) *resour
|
|||
}
|
||||
|
||||
func TestCTXSwitch(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
m.When(ca.Switch("fred")).ThenReturn(nil)
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
m.When(mr.Switch("fred")).ThenReturn(nil)
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
err := ctx.Switch("fred")
|
||||
|
||||
assert.Nil(t, err)
|
||||
ca.VerifyWasCalledOnce().Switch("fred")
|
||||
mr.VerifyWasCalledOnce().Switch("fred")
|
||||
}
|
||||
|
||||
func TestCTXList(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
m.When(ca.List("blee")).ThenReturn(k8s.Collection{*k8sNamedCTX()}, nil)
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
m.When(mr.List("blee")).ThenReturn(k8s.Collection{*k8sNamedCTX()}, nil)
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
cc, err := ctx.List("blee")
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, resource.Columnars{ctx.New(k8sNamedCTX())}, cc)
|
||||
ca.VerifyWasCalledOnce().List("blee")
|
||||
mr.VerifyWasCalledOnce().List("blee")
|
||||
}
|
||||
|
||||
func TestCTXDelete(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
m.When(ca.Delete("", "fred")).ThenReturn(nil)
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
m.When(mr.Delete("", "fred")).ThenReturn(nil)
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
|
||||
assert.Nil(t, ctx.Delete("fred"))
|
||||
ca.VerifyWasCalledOnce().Delete("", "fred")
|
||||
mr.VerifyWasCalledOnce().Delete("", "fred")
|
||||
}
|
||||
|
||||
func TestCTXListHasName(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
l := NewContextListWithArgs("blee", ctx)
|
||||
|
||||
assert.Equal(t, "ctx", l.GetName())
|
||||
}
|
||||
|
||||
func TestCTXListHasNamespace(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
l := NewContextListWithArgs("blee", ctx)
|
||||
|
||||
assert.Equal(t, resource.NotNamespaced, l.GetNamespace())
|
||||
}
|
||||
|
||||
func TestCTXListHasResource(t *testing.T) {
|
||||
conn := NewMockConnection()
|
||||
ca := NewMockSwitchableResource()
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
|
||||
ctx := NewContextWithArgs(conn, ca)
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
l := NewContextListWithArgs("blee", ctx)
|
||||
|
||||
assert.NotNil(t, l.Resource())
|
||||
}
|
||||
|
||||
func TestCTXHeader(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockSwitchableResource()
|
||||
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
|
||||
assert.Equal(t, 4, len(ctx.Header("")))
|
||||
}
|
||||
|
||||
func TestCTXFields(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
m.When(mc.Config()).ThenReturn(k8sConfig())
|
||||
mr := NewMockSwitchableResource()
|
||||
m.When(mr.MustCurrentContextName()).ThenReturn("test")
|
||||
|
||||
ctx := NewContextWithArgs(mc, mr)
|
||||
c := ctx.New(k8sNamedCTX())
|
||||
|
||||
assert.Equal(t, 4, len(c.Fields("")))
|
||||
assert.Equal(t, "test*", c.Fields("")[0])
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
||||
func k8sConfig() *k8s.Config {
|
||||
ctx := "test"
|
||||
f := genericclioptions.ConfigFlags{
|
||||
Context: &ctx,
|
||||
}
|
||||
return k8s.NewConfig(&f)
|
||||
}
|
||||
|
||||
func k8sCTX() *api.Context {
|
||||
return &api.Context{
|
||||
LocationOfOrigin: "fred",
|
||||
|
|
@ -97,13 +128,13 @@ func k8sCTX() *api.Context {
|
|||
}
|
||||
|
||||
func k8sNamedCTX() *k8s.NamedContext {
|
||||
ctx := k8s.NamedContext{
|
||||
Name: "test",
|
||||
Context: &api.Context{
|
||||
return k8s.NewNamedContext(
|
||||
k8sConfig(),
|
||||
"test",
|
||||
&api.Context{
|
||||
LocationOfOrigin: "fred",
|
||||
Cluster: "blee",
|
||||
AuthInfo: "secret",
|
||||
},
|
||||
}
|
||||
return &ctx
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,15 +95,17 @@ func (r *CRD) ExtFields() Properties {
|
|||
i = r.instance
|
||||
)
|
||||
|
||||
meta := i.Object["metadata"].(map[string]interface{})
|
||||
|
||||
if spec, ok := i.Object["spec"].(map[string]interface{}); ok {
|
||||
pp["name"] = meta["name"]
|
||||
if meta, ok := i.Object["metadata"].(map[string]interface{}); ok {
|
||||
pp["name"] = meta["name"]
|
||||
}
|
||||
pp["group"], pp["version"] = spec["group"], spec["version"]
|
||||
names := spec["names"].(map[string]interface{})
|
||||
pp["kind"] = names["kind"]
|
||||
pp["singular"], pp["plural"] = names["singular"], names["plural"]
|
||||
pp["aliases"] = names["shortNames"]
|
||||
|
||||
if names, ok := spec["names"].(map[string]interface{}); ok {
|
||||
pp["kind"] = names["kind"]
|
||||
pp["singular"], pp["plural"] = names["singular"], names["plural"]
|
||||
pp["aliases"] = names["shortNames"]
|
||||
}
|
||||
}
|
||||
|
||||
return pp
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func NewCRDListWithArgs(ns string, r *resource.CRD) resource.List {
|
|||
func NewCRDWithArgs(conn k8s.Connection, res resource.Cruder) *resource.CRD {
|
||||
r := &resource.CRD{Base: resource.NewBase(conn, res)}
|
||||
r.Factory = r
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
@ -38,11 +39,19 @@ func TestCRDListAccess(t *testing.T) {
|
|||
|
||||
func TestCRDFields(t *testing.T) {
|
||||
r := newCRD().Fields("blee")
|
||||
|
||||
assert.Equal(t, "fred", r[0])
|
||||
}
|
||||
|
||||
func TestCRDExtFields(t *testing.T) {
|
||||
p := newCRDFull().ExtFields()
|
||||
|
||||
assert.Equal(t, 7, len(p))
|
||||
}
|
||||
|
||||
func TestCRDFieldsAllNS(t *testing.T) {
|
||||
r := newCRD().Fields(resource.AllNamespaces)
|
||||
|
||||
assert.Equal(t, "fred", r[0])
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +106,33 @@ func k8sCRD() *unstructured.Unstructured {
|
|||
}
|
||||
}
|
||||
|
||||
func k8sCRDFull() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "blee",
|
||||
"name": "fred",
|
||||
"creationTimestamp": "2018-12-14T10:36:43.326972Z",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"names": map[string]interface{}{
|
||||
"kind": "cool",
|
||||
"singular": "cool",
|
||||
"plural": "cools",
|
||||
"shortNamed": []string{"co", "cos"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCRDFull() resource.Columnar {
|
||||
mc := NewMockConnection()
|
||||
return resource.NewCRD(mc).New(k8sCRDFull())
|
||||
}
|
||||
|
||||
func newCRD() resource.Columnar {
|
||||
mc := NewMockConnection()
|
||||
return resource.NewCRD(mc).New(k8sCRD())
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func (r *CronJob) Fields(ns string) Row {
|
|||
return append(ff,
|
||||
i.Name,
|
||||
i.Spec.Schedule,
|
||||
boolToStr(*i.Spec.Suspend),
|
||||
boolPtrToStr(i.Spec.Suspend),
|
||||
strconv.Itoa(len(i.Status.Active)),
|
||||
lastScheduled,
|
||||
toAge(i.ObjectMeta.CreationTimestamp),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,312 @@
|
|||
package resource_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
m "github.com/petergtz/pegomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func NewCustomListWithArgs(ns, name string, r *resource.Custom) resource.List {
|
||||
return resource.NewList(ns, name, r, resource.AllVerbsAccess)
|
||||
}
|
||||
|
||||
func NewCustomWithArgs(conn k8s.Connection, res resource.Cruder) *resource.Custom {
|
||||
r := &resource.Custom{Base: resource.NewBase(conn, res)}
|
||||
r.Factory = r
|
||||
return r
|
||||
}
|
||||
|
||||
func TestCustomListAccess(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
|
||||
ns := "blee"
|
||||
r := NewCustomWithArgs(mc, mr)
|
||||
l := NewCustomListWithArgs(resource.AllNamespaces, "fred", r)
|
||||
l.SetNamespace(ns)
|
||||
|
||||
assert.Equal(t, ns, l.GetNamespace())
|
||||
assert.Equal(t, "fred", l.GetName())
|
||||
for _, a := range []int{resource.GetAccess, resource.ListAccess, resource.DeleteAccess, resource.ViewAccess, resource.EditAccess} {
|
||||
assert.True(t, l.Access(a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomFields(t *testing.T) {
|
||||
r := newCustom().Fields("blee")
|
||||
assert.Equal(t, "a", r[0])
|
||||
}
|
||||
|
||||
func TestCustomMarshal(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
m.When(mr.Get("blee", "fred")).ThenReturn(k8sCustomTable(), nil)
|
||||
|
||||
cm := NewCustomWithArgs(mc, mr)
|
||||
ma, err := cm.Marshal("blee/fred")
|
||||
mr.VerifyWasCalledOnce().Get("blee", "fred")
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, customYaml(), ma)
|
||||
}
|
||||
|
||||
func TestCustomListData(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
m.When(mr.List("blee")).ThenReturn(k8s.Collection{k8sCustomTable()}, nil)
|
||||
|
||||
l := NewCustomListWithArgs("blee", "fred", NewCustomWithArgs(mc, mr))
|
||||
// Make sure we can get deltas!
|
||||
for i := 0; i < 2; i++ {
|
||||
err := l.Reconcile()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
mr.VerifyWasCalled(m.Times(2)).List("blee")
|
||||
td := l.Data()
|
||||
assert.Equal(t, 1, len(td.Rows))
|
||||
assert.Equal(t, "blee", l.GetNamespace())
|
||||
row := td.Rows["blee/fred"]
|
||||
assert.Equal(t, 3, len(row.Deltas))
|
||||
for _, d := range row.Deltas {
|
||||
assert.Equal(t, "", d)
|
||||
}
|
||||
assert.Equal(t, resource.Row{"a"}, row.Fields[:1])
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
||||
func k8sCustomTable() *metav1beta1.Table {
|
||||
return &metav1beta1.Table{
|
||||
ColumnDefinitions: []metav1beta1.TableColumnDefinition{
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
{Name: "C"},
|
||||
},
|
||||
Rows: []metav1beta1.TableRow{
|
||||
{
|
||||
Object: runtime.RawExtension{
|
||||
Raw: []byte(`{
|
||||
"kind": "fred",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"namespace": "blee",
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func k8sCustomRow() *metav1beta1.TableRow {
|
||||
return &metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{
|
||||
Raw: []byte(`{
|
||||
"kind": "fred",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"namespace": "blee",
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCustom() resource.Columnar {
|
||||
mc := NewMockConnection()
|
||||
return resource.NewCustom(mc, "g", "v1", "fred").New(k8sCustomRow())
|
||||
}
|
||||
|
||||
func customYaml() string {
|
||||
return `typemeta:
|
||||
kind: ""
|
||||
apiversion: ""
|
||||
listmeta:
|
||||
selflink: ""
|
||||
resourceversion: ""
|
||||
continue: ""
|
||||
columndefinitions:
|
||||
- name: A
|
||||
type: ""
|
||||
format: ""
|
||||
description: ""
|
||||
priority: 0
|
||||
- name: B
|
||||
type: ""
|
||||
format: ""
|
||||
description: ""
|
||||
priority: 0
|
||||
- name: C
|
||||
type: ""
|
||||
format: ""
|
||||
description: ""
|
||||
priority: 0
|
||||
rows:
|
||||
- cells:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
conditions: []
|
||||
object:
|
||||
raw:
|
||||
- 123
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 34
|
||||
- 107
|
||||
- 105
|
||||
- 110
|
||||
- 100
|
||||
- 34
|
||||
- 58
|
||||
- 32
|
||||
- 34
|
||||
- 102
|
||||
- 114
|
||||
- 101
|
||||
- 100
|
||||
- 34
|
||||
- 44
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 34
|
||||
- 97
|
||||
- 112
|
||||
- 105
|
||||
- 86
|
||||
- 101
|
||||
- 114
|
||||
- 115
|
||||
- 105
|
||||
- 111
|
||||
- 110
|
||||
- 34
|
||||
- 58
|
||||
- 32
|
||||
- 34
|
||||
- 118
|
||||
- 49
|
||||
- 34
|
||||
- 44
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 34
|
||||
- 109
|
||||
- 101
|
||||
- 116
|
||||
- 97
|
||||
- 100
|
||||
- 97
|
||||
- 116
|
||||
- 97
|
||||
- 34
|
||||
- 58
|
||||
- 32
|
||||
- 123
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 34
|
||||
- 110
|
||||
- 97
|
||||
- 109
|
||||
- 101
|
||||
- 115
|
||||
- 112
|
||||
- 97
|
||||
- 99
|
||||
- 101
|
||||
- 34
|
||||
- 58
|
||||
- 32
|
||||
- 34
|
||||
- 98
|
||||
- 108
|
||||
- 101
|
||||
- 101
|
||||
- 34
|
||||
- 44
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 34
|
||||
- 110
|
||||
- 97
|
||||
- 109
|
||||
- 101
|
||||
- 34
|
||||
- 58
|
||||
- 32
|
||||
- 34
|
||||
- 102
|
||||
- 114
|
||||
- 101
|
||||
- 100
|
||||
- 34
|
||||
- 10
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 32
|
||||
- 125
|
||||
- 125
|
||||
object: null
|
||||
`
|
||||
}
|
||||
|
|
@ -121,3 +121,11 @@ func ToMillicore(v int64) string {
|
|||
func ToMi(v float64) string {
|
||||
return strconv.Itoa(int(v)) + "Mi"
|
||||
}
|
||||
|
||||
func boolPtrToStr(b *bool) string {
|
||||
if b == nil {
|
||||
return "false"
|
||||
}
|
||||
|
||||
return boolToStr(*b)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,23 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBoolPtrToStr(t *testing.T) {
|
||||
tv, fv := true, false
|
||||
|
||||
uu := []struct {
|
||||
p *bool
|
||||
e string
|
||||
}{
|
||||
{nil, "false"},
|
||||
{&tv, "true"},
|
||||
{&fv, "false"},
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, boolPtrToStr(u.p))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaced(t *testing.T) {
|
||||
uu := []struct {
|
||||
p, ns, n string
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package resource_test
|
|||
import (
|
||||
k8s "github.com/derailed/k9s/internal/k8s"
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
dynamic "k8s.io/client-go/dynamic"
|
||||
kubernetes "k8s.io/client-go/kubernetes"
|
||||
rest "k8s.io/client-go/rest"
|
||||
|
|
@ -97,6 +98,25 @@ func (mock *MockClusterMeta) DynDialOrDie() dynamic.Interface {
|
|||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockClusterMeta) FetchNodes() ([]v1.Node, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
|
||||
}
|
||||
params := []pegomock.Param{}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("FetchNodes", params, []reflect.Type{reflect.TypeOf((*[]v1.Node)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 []v1.Node
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].([]v1.Node)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockClusterMeta) HasMetrics() bool {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
|
||||
|
|
@ -355,6 +375,23 @@ func (c *ClusterMeta_DynDialOrDie_OngoingVerification) GetCapturedArguments() {
|
|||
func (c *ClusterMeta_DynDialOrDie_OngoingVerification) GetAllCapturedArguments() {
|
||||
}
|
||||
|
||||
func (verifier *VerifierClusterMeta) FetchNodes() *ClusterMeta_FetchNodes_OngoingVerification {
|
||||
params := []pegomock.Param{}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "FetchNodes", params, verifier.timeout)
|
||||
return &ClusterMeta_FetchNodes_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type ClusterMeta_FetchNodes_OngoingVerification struct {
|
||||
mock *MockClusterMeta
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *ClusterMeta_FetchNodes_OngoingVerification) GetCapturedArguments() {
|
||||
}
|
||||
|
||||
func (c *ClusterMeta_FetchNodes_OngoingVerification) GetAllCapturedArguments() {
|
||||
}
|
||||
|
||||
func (verifier *VerifierClusterMeta) HasMetrics() *ClusterMeta_HasMetrics_OngoingVerification {
|
||||
params := []pegomock.Param{}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "HasMetrics", params, verifier.timeout)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,21 @@ func (mock *MockSwitchableResource) List(_param0 string) (k8s.Collection, error)
|
|||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockSwitchableResource) MustCurrentContextName() string {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockSwitchableResource().")
|
||||
}
|
||||
params := []pegomock.Param{}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("MustCurrentContextName", 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 *MockSwitchableResource) Switch(_param0 string) error {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockSwitchableResource().")
|
||||
|
|
@ -212,6 +227,23 @@ func (c *SwitchableResource_List_OngoingVerification) GetAllCapturedArguments()
|
|||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierSwitchableResource) MustCurrentContextName() *SwitchableResource_MustCurrentContextName_OngoingVerification {
|
||||
params := []pegomock.Param{}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "MustCurrentContextName", params, verifier.timeout)
|
||||
return &SwitchableResource_MustCurrentContextName_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type SwitchableResource_MustCurrentContextName_OngoingVerification struct {
|
||||
mock *MockSwitchableResource
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *SwitchableResource_MustCurrentContextName_OngoingVerification) GetCapturedArguments() {
|
||||
}
|
||||
|
||||
func (c *SwitchableResource_MustCurrentContextName_OngoingVerification) GetAllCapturedArguments() {
|
||||
}
|
||||
|
||||
func (verifier *VerifierSwitchableResource) Switch(_param0 string) *SwitchableResource_Switch_OngoingVerification {
|
||||
params := []pegomock.Param{_param0}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Switch", params, verifier.timeout)
|
||||
|
|
|
|||
|
|
@ -207,11 +207,12 @@ func (r *Pod) Fields(ns string) Row {
|
|||
ff = append(ff, i.Namespace)
|
||||
}
|
||||
|
||||
cr, _, rc, cc := r.statuses()
|
||||
ss := i.Status.ContainerStatuses
|
||||
cr, _, rc := r.statuses(ss)
|
||||
|
||||
return append(ff,
|
||||
Pad(i.ObjectMeta.Name, podNameSize),
|
||||
strconv.Itoa(cr)+"/"+strconv.Itoa(len(cc)),
|
||||
strconv.Itoa(cr)+"/"+strconv.Itoa(len(ss)),
|
||||
r.phase(i.Status),
|
||||
strconv.Itoa(rc),
|
||||
ToMillicore(r.metrics.CurrentCPU),
|
||||
|
|
@ -226,97 +227,8 @@ func (r *Pod) Fields(ns string) Row {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func (r *Pod) toVolumes(vv []v1.Volume) map[string]interface{} {
|
||||
m := make(map[string]interface{}, len(vv))
|
||||
for _, v := range vv {
|
||||
m[v.Name] = r.toVolume(v)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (r *Pod) toVolume(v v1.Volume) map[string]interface{} {
|
||||
switch {
|
||||
case v.Secret != nil:
|
||||
return map[string]interface{}{
|
||||
"Type": "Secret",
|
||||
"Name": v.Secret.SecretName,
|
||||
"Optional": r.boolPtrToStr(v.Secret.Optional),
|
||||
}
|
||||
case v.AWSElasticBlockStore != nil:
|
||||
return map[string]interface{}{
|
||||
"Type": v.AWSElasticBlockStore.FSType,
|
||||
"VolumeID": v.AWSElasticBlockStore.VolumeID,
|
||||
"Partition": strconv.Itoa(int(v.AWSElasticBlockStore.Partition)),
|
||||
"ReadOnly": boolToStr(v.AWSElasticBlockStore.ReadOnly),
|
||||
}
|
||||
default:
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Pod) toContainers(cc []v1.Container) map[string]interface{} {
|
||||
m := make(map[string]interface{}, len(cc))
|
||||
for _, c := range cc {
|
||||
m[c.Name] = map[string]interface{}{
|
||||
"Image": c.Image,
|
||||
"Environment": r.toEnv(c.Env),
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (r *Pod) toEnv(ee []v1.EnvVar) []string {
|
||||
if len(ee) == 0 {
|
||||
return []string{MissingValue}
|
||||
}
|
||||
|
||||
ss := make([]string, len(ee))
|
||||
for i, e := range ee {
|
||||
s := r.toEnvFrom(e.ValueFrom)
|
||||
if len(s) == 0 {
|
||||
ss[i] = e.Name + "=" + e.Value
|
||||
} else {
|
||||
ss[i] = e.Name + "=" + e.Value + "(" + s + ")"
|
||||
}
|
||||
}
|
||||
|
||||
return ss
|
||||
}
|
||||
|
||||
func (r *Pod) toEnvFrom(e *v1.EnvVarSource) string {
|
||||
if e == nil {
|
||||
return MissingValue
|
||||
}
|
||||
|
||||
var s string
|
||||
switch {
|
||||
case e.ConfigMapKeyRef != nil:
|
||||
f := e.ConfigMapKeyRef
|
||||
s += f.Name + ":" + f.Key + "(" + r.boolPtrToStr(f.Optional) + ")"
|
||||
case e.FieldRef != nil:
|
||||
f := e.FieldRef
|
||||
s += f.FieldPath + ":" + f.APIVersion
|
||||
case e.SecretKeyRef != nil:
|
||||
f := e.SecretKeyRef
|
||||
s += f.Name + ":" + f.Key + "(" + r.boolPtrToStr(f.Optional) + ")"
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *Pod) boolPtrToStr(b *bool) string {
|
||||
if b == nil {
|
||||
return "false"
|
||||
}
|
||||
|
||||
return boolToStr(*b)
|
||||
}
|
||||
|
||||
func (r *Pod) statuses() (cr, ct, rc int, cc []v1.ContainerStatus) {
|
||||
cc = r.instance.Status.ContainerStatuses
|
||||
for _, c := range cc {
|
||||
func (r *Pod) statuses(ss []v1.ContainerStatus) (cr, ct, rc int) {
|
||||
for _, c := range ss {
|
||||
if c.State.Terminated != nil {
|
||||
ct++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestPodStatuses(t *testing.T) {
|
||||
type counts struct {
|
||||
ready, terminated, restarts int
|
||||
}
|
||||
|
||||
uu := []struct {
|
||||
s []v1.ContainerStatus
|
||||
e counts
|
||||
}{
|
||||
{
|
||||
[]v1.ContainerStatus{
|
||||
v1.ContainerStatus{
|
||||
Name: "c1",
|
||||
Ready: true,
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
},
|
||||
v1.ContainerStatus{
|
||||
Name: "c2",
|
||||
Ready: false,
|
||||
RestartCount: 10,
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
counts{1, 1, 10},
|
||||
},
|
||||
}
|
||||
|
||||
var p Pod
|
||||
for _, u := range uu {
|
||||
cr, ct, cs := p.statuses(u.s)
|
||||
assert.Equal(t, u.e.ready, cr)
|
||||
assert.Equal(t, u.e.terminated, ct)
|
||||
assert.Equal(t, u.e.restarts, cs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodPhase(t *testing.T) {
|
||||
uu := []struct {
|
||||
s v1.PodStatus
|
||||
e string
|
||||
}{
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Running",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{
|
||||
Reason: "blee",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blee",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Terminating",
|
||||
},
|
||||
{
|
||||
v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: "c1",
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: "blee",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blee",
|
||||
},
|
||||
}
|
||||
|
||||
var p Pod
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, p.phase(u.s))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package resource_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
m "github.com/petergtz/pegomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func NewRCListWithArgs(ns string, r *resource.ReplicationController) resource.List {
|
||||
return resource.NewList(ns, "rc", r, resource.AllVerbsAccess|resource.DescribeAccess)
|
||||
}
|
||||
|
||||
func NewRCWithArgs(conn k8s.Connection, res resource.Cruder) *resource.ReplicationController {
|
||||
r := &resource.ReplicationController{Base: resource.NewBase(conn, res)}
|
||||
r.Factory = r
|
||||
return r
|
||||
}
|
||||
|
||||
func TestRCListAccess(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
|
||||
ns := "blee"
|
||||
l := NewRCListWithArgs(resource.AllNamespaces, NewRCWithArgs(mc, mr))
|
||||
l.SetNamespace(ns)
|
||||
|
||||
assert.Equal(t, "blee", l.GetNamespace())
|
||||
assert.Equal(t, "rc", l.GetName())
|
||||
for _, a := range []int{resource.GetAccess, resource.ListAccess, resource.DeleteAccess, resource.ViewAccess, resource.EditAccess} {
|
||||
assert.True(t, l.Access(a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRCFields(t *testing.T) {
|
||||
r := newRC().Fields("blee")
|
||||
assert.Equal(t, "fred", r[0])
|
||||
}
|
||||
|
||||
func TestRCMarshal(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
m.When(mr.Get("blee", "fred")).ThenReturn(k8sRC(), nil)
|
||||
|
||||
cm := NewRCWithArgs(mc, mr)
|
||||
ma, err := cm.Marshal("blee/fred")
|
||||
|
||||
mr.VerifyWasCalledOnce().Get("blee", "fred")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, rcYaml(), ma)
|
||||
}
|
||||
|
||||
func TestRCListData(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
mr := NewMockCruder()
|
||||
m.When(mr.List("blee")).ThenReturn(k8s.Collection{*k8sRC()}, nil)
|
||||
|
||||
l := NewRCListWithArgs("blee", NewRCWithArgs(mc, mr))
|
||||
// Make sure we mrn get deltas!
|
||||
for i := 0; i < 2; i++ {
|
||||
err := l.Reconcile()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
mr.VerifyWasCalled(m.Times(2)).List("blee")
|
||||
td := l.Data()
|
||||
assert.Equal(t, 1, len(td.Rows))
|
||||
assert.Equal(t, "blee", l.GetNamespace())
|
||||
row := td.Rows["blee/fred"]
|
||||
assert.Equal(t, 5, len(row.Deltas))
|
||||
for _, d := range row.Deltas {
|
||||
assert.Equal(t, "", d)
|
||||
}
|
||||
assert.Equal(t, resource.Row{"fred"}, row.Fields[:1])
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
||||
func k8sRC() *v1.ReplicationController {
|
||||
var c int32 = 10
|
||||
return &v1.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "blee",
|
||||
Name: "fred",
|
||||
CreationTimestamp: metav1.Time{Time: testTime()},
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: &c,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newRC() resource.Columnar {
|
||||
mc := NewMockConnection()
|
||||
return resource.NewReplicationController(mc).New(k8sRC())
|
||||
}
|
||||
|
||||
func rcYaml() string {
|
||||
return `apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
creationTimestamp: "2018-12-14T17:36:43Z"
|
||||
name: fred
|
||||
namespace: blee
|
||||
spec:
|
||||
replicas: 10
|
||||
status:
|
||||
replicas: 0
|
||||
`
|
||||
}
|
||||
|
|
@ -76,6 +76,7 @@ func (*Role) Header(ns string) Row {
|
|||
// Fields retrieves displayable fields.
|
||||
func (r *Role) Fields(ns string) Row {
|
||||
ff := make(Row, 0, len(r.Header(ns)))
|
||||
|
||||
i := r.instance
|
||||
if ns == AllNamespaces {
|
||||
ff = append(ff, i.Namespace)
|
||||
|
|
@ -92,12 +93,13 @@ func (r *Role) Fields(ns string) Row {
|
|||
|
||||
func (r *Role) parseRules(pp []v1.PolicyRule) []Row {
|
||||
acc := make([]Row, len(pp))
|
||||
|
||||
for i, p := range pp {
|
||||
acc[i] = make(Row, 4)
|
||||
acc[i][0] = strings.Join(p.Resources, ", ")
|
||||
acc[i][1] = strings.Join(p.NonResourceURLs, ", ")
|
||||
acc[i][2] = strings.Join(p.ResourceNames, ", ")
|
||||
acc[i][3] = strings.Join(p.Verbs, ", ")
|
||||
acc[i] = make(Row, 0, 4)
|
||||
acc[i] = append(acc[i], strings.Join(p.Resources, ", "))
|
||||
acc[i] = append(acc[i], strings.Join(p.NonResourceURLs, ", "))
|
||||
acc[i] = append(acc[i], strings.Join(p.ResourceNames, ", "))
|
||||
acc[i] = append(acc[i], strings.Join(p.Verbs, ", "))
|
||||
}
|
||||
|
||||
return acc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/rbac/v1"
|
||||
)
|
||||
|
||||
func TestRoleParseRules(t *testing.T) {
|
||||
rules := []v1.PolicyRule{
|
||||
{
|
||||
Resources: []string{"", "apps"},
|
||||
NonResourceURLs: []string{"/fred"},
|
||||
ResourceNames: []string{"pods", "deployments"},
|
||||
Verbs: []string{"get", "list"},
|
||||
},
|
||||
}
|
||||
|
||||
var r Role
|
||||
rows := r.parseRules(rules)
|
||||
|
||||
assert.Equal(t, 1, len(rows))
|
||||
assert.Equal(t, 1, len(rows))
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ func (r *Service) Fields(ns string) Row {
|
|||
i.ObjectMeta.Name,
|
||||
string(i.Spec.Type),
|
||||
i.Spec.ClusterIP,
|
||||
r.toIPs(i.Spec.Type, getSvcExtIPS(i)),
|
||||
r.toIPs(i.Spec.Type, r.getSvcExtIPS(i)),
|
||||
r.toPorts(i.Spec.Ports),
|
||||
toAge(i.ObjectMeta.CreationTimestamp),
|
||||
)
|
||||
|
|
@ -107,7 +107,7 @@ func (r *Service) Fields(ns string) Row {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func getSvcExtIPS(svc *v1.Service) []string {
|
||||
func (r *Service) getSvcExtIPS(svc *v1.Service) []string {
|
||||
results := []string{}
|
||||
|
||||
switch svc.Spec.Type {
|
||||
|
|
@ -116,7 +116,7 @@ func getSvcExtIPS(svc *v1.Service) []string {
|
|||
case v1.ServiceTypeNodePort:
|
||||
return svc.Spec.ExternalIPs
|
||||
case v1.ServiceTypeLoadBalancer:
|
||||
lbIps := lbIngressIP(svc.Status.LoadBalancer)
|
||||
lbIps := r.lbIngressIP(svc.Status.LoadBalancer)
|
||||
if len(svc.Spec.ExternalIPs) > 0 {
|
||||
if len(lbIps) > 0 {
|
||||
results = append(results, lbIps)
|
||||
|
|
@ -133,7 +133,7 @@ func getSvcExtIPS(svc *v1.Service) []string {
|
|||
return results
|
||||
}
|
||||
|
||||
func lbIngressIP(s v1.LoadBalancerStatus) string {
|
||||
func (*Service) lbIngressIP(s v1.LoadBalancerStatus) string {
|
||||
ingress := s.Ingress
|
||||
result := []string{}
|
||||
for i := range ingress {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,37 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestToIPs(t *testing.T) {
|
||||
s := Service{}
|
||||
func TestSvcExtIPs(t *testing.T) {
|
||||
i := k8sSVCLb()
|
||||
|
||||
var s Service
|
||||
ips := s.getSvcExtIPS(i)
|
||||
|
||||
assert.Equal(t, "10.0.0.0,2.2.2.2", s.toIPs(i.Spec.Type, ips))
|
||||
}
|
||||
|
||||
func TestLbIngressIP(t *testing.T) {
|
||||
lb := v1.LoadBalancerStatus{
|
||||
Ingress: []v1.LoadBalancerIngress{
|
||||
{"10.0.0.0", "fred"},
|
||||
{"10.0.0.1", "blee"},
|
||||
},
|
||||
}
|
||||
|
||||
var s Service
|
||||
assert.Equal(t, "10.0.0.0,10.0.0.1", s.lbIngressIP(lb))
|
||||
}
|
||||
|
||||
func TestToIPs(t *testing.T) {
|
||||
uu := []struct {
|
||||
t v1.ServiceType
|
||||
ii []string
|
||||
|
|
@ -19,14 +41,14 @@ func TestToIPs(t *testing.T) {
|
|||
{v1.ServiceTypeLoadBalancer, []string{}, "<pending>"},
|
||||
{v1.ServiceTypeClusterIP, []string{}, MissingValue},
|
||||
}
|
||||
|
||||
var s Service
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, s.toIPs(u.t, u.ii))
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPorts(t *testing.T) {
|
||||
var s Service
|
||||
|
||||
uu := []struct {
|
||||
pp []v1.ServicePort
|
||||
e string
|
||||
|
|
@ -40,13 +62,14 @@ func TestToPorts(t *testing.T) {
|
|||
"80►30080╱UDP",
|
||||
},
|
||||
}
|
||||
|
||||
var s Service
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, s.toPorts(u.pp))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToPorts(b *testing.B) {
|
||||
var s Service
|
||||
sp := []v1.ServicePort{
|
||||
{Name: "http", Port: 80, NodePort: 90, Protocol: "TCP"},
|
||||
{Port: 80, NodePort: 90, Protocol: "TCP"},
|
||||
|
|
@ -55,7 +78,46 @@ func BenchmarkToPorts(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
var s Service
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.toPorts(sp)
|
||||
}
|
||||
}
|
||||
|
||||
func k8sSVCLb() *v1.Service {
|
||||
return &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fred",
|
||||
Namespace: "blee",
|
||||
CreationTimestamp: metav1.Time{testTime()},
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeLoadBalancer,
|
||||
ClusterIP: "1.1.1.1",
|
||||
ExternalIPs: []string{"2.2.2.2"},
|
||||
Selector: map[string]string{"fred": "blee"},
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 90,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1.ServiceStatus{
|
||||
LoadBalancer: v1.LoadBalancerStatus{
|
||||
Ingress: []v1.LoadBalancerIngress{
|
||||
{IP: "10.0.0.0", Hostname: "fred"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testTime() time.Time {
|
||||
t, err := time.Parse(time.RFC3339, "2018-12-14T10:36:43.326972-07:00")
|
||||
if err != nil {
|
||||
fmt.Println("TestTime Failed", err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue