improve cm and secret speed by fetching tabular data only

mine
Gustavo Paiva 2019-10-06 04:19:13 -03:00
parent 196884a9a8
commit 063f6cd3f0
6 changed files with 2 additions and 632 deletions

View File

@ -1,44 +0,0 @@
package k8s
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConfigMap represents a Kubernetes ConfigMap
type ConfigMap struct {
*base
Connection
}
// NewConfigMap returns a new ConfigMap.
func NewConfigMap(c Connection) *ConfigMap {
return &ConfigMap{&base{}, c}
}
// Get a ConfigMap.
func (c *ConfigMap) Get(ns, n string) (interface{}, error) {
return c.DialOrDie().CoreV1().ConfigMaps(ns).Get(n, metav1.GetOptions{})
}
// List all ConfigMaps in a given namespace.
func (c *ConfigMap) List(ns string) (Collection, error) {
opts := metav1.ListOptions{
LabelSelector: c.labelSelector,
FieldSelector: c.fieldSelector,
}
rr, err := c.DialOrDie().CoreV1().ConfigMaps(ns).List(opts)
if err != nil {
return nil, err
}
cc := make(Collection, len(rr.Items))
for i, r := range rr.Items {
cc[i] = r
}
return cc, nil
}
// Delete a ConfigMap.
func (c *ConfigMap) Delete(ns, n string, cascade, force bool) error {
return c.DialOrDie().CoreV1().ConfigMaps(ns).Delete(n, nil)
}

View File

@ -1,44 +0,0 @@
package k8s
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Secret represents a Kubernetes Secret.
type Secret struct {
*base
Connection
}
// NewSecret returns a new Secret.
func NewSecret(c Connection) *Secret {
return &Secret{&base{}, c}
}
// Get a Secret.
func (s *Secret) Get(ns, n string) (interface{}, error) {
return s.DialOrDie().CoreV1().Secrets(ns).Get(n, metav1.GetOptions{})
}
// List all Secrets in a given namespace.
func (s *Secret) List(ns string) (Collection, error) {
opts := metav1.ListOptions{
LabelSelector: s.labelSelector,
FieldSelector: s.fieldSelector,
}
rr, err := s.DialOrDie().CoreV1().Secrets(ns).List(opts)
if err != nil {
return nil, err
}
cc := make(Collection, len(rr.Items))
for i, r := range rr.Items {
cc[i] = r
}
return cc, nil
}
// Delete a Secret.
func (s *Secret) Delete(ns, n string, cascade, force bool) error {
return s.DialOrDie().CoreV1().Secrets(ns).Delete(n, nil)
}

View File

@ -1,96 +1,6 @@
package resource
import (
"log"
"strconv"
"github.com/derailed/k9s/internal/k8s"
v1 "k8s.io/api/core/v1"
)
// ConfigMap tracks a kubernetes resource.
type ConfigMap struct {
*Base
instance *v1.ConfigMap
}
// NewConfigMapList returns a new resource list.
func NewConfigMapList(c Connection, ns string) List {
return NewList(
ns,
"cm",
NewConfigMap(c),
AllVerbsAccess|DescribeAccess,
)
}
// NewConfigMap instantiates a new ConfigMap.
func NewConfigMap(c Connection) *ConfigMap {
m := &ConfigMap{&Base{Connection: c, Resource: k8s.NewConfigMap(c)}, nil}
m.Factory = m
return m
}
// New builds a new ConfigMap instance from a k8s resource.
func (r *ConfigMap) New(i interface{}) Columnar {
cm := NewConfigMap(r.Connection)
switch instance := i.(type) {
case *v1.ConfigMap:
cm.instance = instance
case v1.ConfigMap:
cm.instance = &instance
default:
log.Fatalf("Unknown %#v", i)
}
cm.path = cm.namespacedName(cm.instance.ObjectMeta)
return cm
}
// Marshal resource to yaml.
func (r *ConfigMap) Marshal(path string) (string, error) {
ns, n := Namespaced(path)
i, err := r.Resource.Get(ns, n)
if err != nil {
return "", err
}
cm := i.(*v1.ConfigMap)
cm.TypeMeta.APIVersion = "v1"
cm.TypeMeta.Kind = "ConfigMap"
return r.marshalObject(cm)
}
// Header return resource header.
func (*ConfigMap) Header(ns string) Row {
hh := Row{}
if ns == AllNamespaces {
hh = append(hh, "NAMESPACE")
}
return append(hh, "NAME", "DATA", "AGE")
}
// NumCols designates if column is numerical.
func (*ConfigMap) NumCols(n string) map[string]bool {
return map[string]bool{
"DATA": true,
}
}
// Fields retrieves displayable fields.
func (r *ConfigMap) Fields(ns string) Row {
ff := make(Row, 0, len(r.Header(ns)))
i := r.instance
if ns == AllNamespaces {
ff = append(ff, i.Namespace)
}
return append(ff,
i.Name,
strconv.Itoa(len(i.Data)),
toAge(i.ObjectMeta.CreationTimestamp),
)
return NewCustomList(c, true, "", "v1/configmaps")
}

View File

@ -1,174 +0,0 @@
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 NewConfigMapListWithArgs(ns string, r *resource.ConfigMap) resource.List {
return resource.NewList(ns, "cm", r, resource.AllVerbsAccess|resource.DescribeAccess)
}
func NewConfigMapWithArgs(rc k8s.Connection, res resource.Cruder) *resource.ConfigMap {
r := &resource.ConfigMap{Base: resource.NewBase(rc, res)}
r.Factory = r
return r
}
func TestCMFieldsAllNS(t *testing.T) {
r := newConfigMap().Fields(resource.AllNamespaces)
assert.Equal(t, "blee", r[0])
assert.Equal(t, "fred", r[1])
assert.Equal(t, "2", r[2])
}
func TestCMFields(t *testing.T) {
r := newConfigMap().Fields("blee")
assert.Equal(t, "fred", r[0])
assert.Equal(t, "2", r[1])
}
func TestCMGet(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
m.When(cr.Get("blee", "fred")).ThenReturn(k8sCM(), nil)
cm := NewConfigMapWithArgs(rc, cr)
ma, err := cm.Get("blee/fred")
assert.Nil(t, err)
cr.VerifyWasCalledOnce().Get("blee", "fred")
assert.Equal(t, cm.New(k8sCM()), ma)
}
func TestCMList(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
m.When(cr.List("blee")).ThenReturn(k8s.Collection{*k8sCM()}, nil)
cm := NewConfigMapWithArgs(rc, cr)
ma, err := cm.List("blee")
assert.Nil(t, err)
cr.VerifyWasCalledOnce().List("blee")
assert.Equal(t, resource.Columnars{cm.New(k8sCM())}, ma)
}
func TestCMDelete(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
m.When(cr.Delete("blee", "fred", true, true)).ThenReturn(nil)
cm := NewConfigMapWithArgs(rc, cr)
assert.Nil(t, cm.Delete("blee/fred", true, true))
cr.VerifyWasCalledOnce().Delete("blee", "fred", true, true)
}
func TestCMMarshal(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
m.When(cr.Get("blee", "fred")).ThenReturn(k8sCM(), nil)
cm := NewConfigMapWithArgs(rc, cr)
ma, err := cm.Marshal("blee/fred")
cr.VerifyWasCalledOnce().Get("blee", "fred")
assert.Nil(t, err)
assert.Equal(t, cmYaml(), ma)
}
func TestCMListHasName(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
cm := NewConfigMapWithArgs(rc, cr)
l := NewConfigMapListWithArgs("blee", cm)
assert.Equal(t, "cm", l.GetName())
}
func TestCMListHasNamespace(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
cm := NewConfigMapWithArgs(rc, cr)
l := NewConfigMapListWithArgs("blee", cm)
assert.Equal(t, "blee", l.GetNamespace())
}
func TestCMListHasResource(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
cm := NewConfigMapWithArgs(rc, cr)
l := NewConfigMapListWithArgs("blee", cm)
assert.NotNil(t, l.Resource())
}
func TestCMListData(t *testing.T) {
rc := NewMockConnection()
cr := NewMockCruder()
m.When(cr.List("blee")).ThenReturn(k8s.Collection{*k8sCM()}, nil)
cm := NewConfigMapWithArgs(rc, cr)
l := NewConfigMapListWithArgs("blee", cm)
// Make sure we crn get deltas!
for i := 0; i < 2; i++ {
err := l.Reconcile(nil, nil)
assert.Nil(t, err)
}
cr.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{"fred", "2"}, row.Fields[:2])
}
// Helpers...
func newConfigMap() resource.Columnar {
rc := NewMockConnection()
return resource.NewConfigMap(rc).New(k8sCM())
}
func k8sCM() *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "fred",
Namespace: "blee",
CreationTimestamp: metav1.Time{testTime()},
},
Data: map[string]string{"blee": "blee", "duh": "duh"},
}
}
func cmYaml() string {
return `apiVersion: v1
data:
blee: blee
duh: duh
kind: ConfigMap
metadata:
creationTimestamp: "2018-12-14T17:36:43Z"
name: fred
namespace: blee
`
}

View File

@ -1,97 +1,6 @@
package resource
import (
"strconv"
"github.com/derailed/k9s/internal/k8s"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
)
// Secret tracks a kubernetes resource.
type Secret struct {
*Base
instance *v1.Secret
}
// NewSecretList returns a new resource list.
func NewSecretList(c Connection, ns string) List {
return NewList(
ns,
"secret",
NewSecret(c),
AllVerbsAccess|DescribeAccess,
)
}
// NewSecret instantiates a new Secret.
func NewSecret(c Connection) *Secret {
s := &Secret{&Base{Connection: c, Resource: k8s.NewSecret(c)}, nil}
s.Factory = s
return s
}
// New builds a new Secret instance from a k8s resource.
func (r *Secret) New(i interface{}) Columnar {
c := NewSecret(r.Connection)
switch instance := i.(type) {
case *v1.Secret:
c.instance = instance
case v1.Secret:
c.instance = &instance
default:
log.Fatal().Msgf("unknown Secret type %#v", i)
}
c.path = c.namespacedName(c.instance.ObjectMeta)
return c
}
// Marshal resource to yaml.
func (r *Secret) Marshal(path string) (string, error) {
ns, n := Namespaced(path)
i, err := r.Resource.Get(ns, n)
if err != nil {
return "", err
}
sec := i.(*v1.Secret)
sec.TypeMeta.APIVersion = "v1"
sec.TypeMeta.Kind = "Secret"
return r.marshalObject(sec)
}
// Header return resource header.
func (*Secret) Header(ns string) Row {
hh := Row{}
if ns == AllNamespaces {
hh = append(hh, "NAMESPACE")
}
return append(hh, "NAME", "TYPE", "DATA", "AGE")
}
// NumCols designates if column is numerical.
func (*Secret) NumCols(n string) map[string]bool {
return map[string]bool{
"DATA": true,
}
}
// Fields retrieves displayable fields.
func (r *Secret) Fields(ns string) Row {
ff := make(Row, 0, len(r.Header(ns)))
i := r.instance
if ns == AllNamespaces {
ff = append(ff, i.Namespace)
}
return append(ff,
i.Name,
string(i.Type),
strconv.Itoa(len(i.Data)),
toAge(i.ObjectMeta.CreationTimestamp),
)
return NewCustomList(c, true, "", "v1/secrets")
}

View File

@ -1,187 +0,0 @@
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 NewSecretListWithArgs(ns string, r *resource.Secret) resource.List {
return resource.NewList(ns, "secret", r, resource.AllVerbsAccess|resource.DescribeAccess)
}
func NewSecretWithArgs(conn k8s.Connection, res resource.Cruder) *resource.Secret {
r := &resource.Secret{Base: resource.NewBase(conn, res)}
r.Factory = r
return r
}
func TestSecretHeader(t *testing.T) {
assert.Equal(t,
resource.Row{"NAME", "TYPE", "DATA", "AGE"},
newSecret().Header(resource.DefaultNamespace),
)
}
func TestSecretHeaderAllNS(t *testing.T) {
assert.Equal(t,
resource.Row{"NAMESPACE", "NAME", "TYPE", "DATA", "AGE"},
newSecret().Header(resource.AllNamespaces),
)
}
func TestSecretFieldsAllNS(t *testing.T) {
r := newSecret().Fields(resource.AllNamespaces)
assert.Equal(t, "blee", r[0])
assert.Equal(t, "fred", r[1])
assert.Equal(t, "Opaque", r[2])
assert.Equal(t, "2", r[3])
}
func TestSecretFields(t *testing.T) {
r := newSecret().Fields("blee")
assert.Equal(t, "fred", r[0])
assert.Equal(t, "Opaque", r[1])
assert.Equal(t, "2", r[2])
}
func TestSecretGet(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
m.When(mr.Get("blee", "fred")).ThenReturn(k8sSecret(), nil)
cm := NewSecretWithArgs(mc, mr)
ma, err := cm.Get("blee/fred")
assert.Nil(t, err)
mr.VerifyWasCalledOnce().Get("blee", "fred")
assert.Equal(t, cm.New(k8sSecret()), ma)
}
func TestSecretList(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
m.When(mr.List("blee")).ThenReturn(k8s.Collection{*k8sSecret()}, nil)
cm := NewSecretWithArgs(mc, mr)
ma, err := cm.List("blee")
assert.Nil(t, err)
mr.VerifyWasCalledOnce().List("blee")
assert.Equal(t, resource.Columnars{cm.New(k8sSecret())}, ma)
}
func TestSecretDelete(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
m.When(mr.Delete("blee", "fred", true, true)).ThenReturn(nil)
cm := NewSecretWithArgs(mc, mr)
assert.Nil(t, cm.Delete("blee/fred", true, true))
mr.VerifyWasCalledOnce().Delete("blee", "fred", true, true)
}
func TestSecretMarshal(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
m.When(mr.Get("blee", "fred")).ThenReturn(k8sSecret(), nil)
cm := NewSecretWithArgs(mc, mr)
ma, err := cm.Marshal("blee/fred")
mr.VerifyWasCalledOnce().Get("blee", "fred")
assert.Nil(t, err)
assert.Equal(t, secretYaml(), ma)
}
func TestSecretListHasName(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
l := NewSecretListWithArgs("blee", NewSecretWithArgs(mc, mr))
assert.Equal(t, "secret", l.GetName())
}
func TestSecretListHasNamespace(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
l := NewSecretListWithArgs("blee", NewSecretWithArgs(mc, mr))
assert.Equal(t, "blee", l.GetNamespace())
}
func TestSecretListHasResource(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
l := NewSecretListWithArgs("blee", NewSecretWithArgs(mc, mr))
assert.NotNil(t, l.Resource())
}
func TestSecretListData(t *testing.T) {
mc := NewMockConnection()
mr := NewMockCruder()
m.When(mr.List("blee")).ThenReturn(k8s.Collection{*k8sSecret()}, nil)
l := NewSecretListWithArgs("blee", NewSecretWithArgs(mc, mr))
// Make sure we mrn get deltas!
for i := 0; i < 2; i++ {
err := l.Reconcile(nil, nil)
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, 4, len(row.Deltas))
for _, d := range row.Deltas {
assert.Equal(t, "", d)
}
assert.Equal(t, resource.Row{"fred", "Opaque", "2"}, row.Fields[:3])
}
// Helpers...
func newSecret() resource.Columnar {
mc := NewMockConnection()
return resource.NewSecret(mc).New(k8sSecret())
}
func k8sSecret() *v1.Secret {
secrets := map[string]string{"blee": "blee", "duh": "duh"}
data := map[string][]byte{}
for k, v := range secrets {
data[k] = []byte(v)
}
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "fred",
Namespace: "blee",
CreationTimestamp: metav1.Time{testTime()},
},
Type: v1.SecretTypeOpaque,
Data: data,
}
}
func secretYaml() string {
return `apiVersion: v1
data:
blee: YmxlZQ==
duh: ZHVo
kind: Secret
metadata:
creationTimestamp: "2018-12-14T17:36:43Z"
name: fred
namespace: blee
type: Opaque
`
}