checkpoint
parent
add0d678f0
commit
99fa0e9952
2
go.mod
2
go.mod
|
|
@ -36,7 +36,7 @@ require (
|
|||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell v1.3.0
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
|
||||
github.com/golang/mock v1.2.0
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ func (g GVR) ToRAndG() (string, string) {
|
|||
tokens := strings.Split(string(g), "/")
|
||||
switch len(tokens) {
|
||||
case 3:
|
||||
return tokens[0], tokens[2]
|
||||
return tokens[2], tokens[0]
|
||||
case 2:
|
||||
return "", tokens[1]
|
||||
return tokens[1], "core"
|
||||
default:
|
||||
return "", tokens[0]
|
||||
return tokens[0], "core"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ type Aliases struct {
|
|||
|
||||
// NewAliases return a new alias.
|
||||
func NewAliases() Aliases {
|
||||
aa := Aliases{Alias: make(Alias, 50)}
|
||||
aa.loadDefaults()
|
||||
return aa
|
||||
return Aliases{
|
||||
Alias: make(Alias, 50),
|
||||
}
|
||||
}
|
||||
|
||||
func (a Aliases) loadDefaults() {
|
||||
|
|
@ -81,6 +81,7 @@ func (a Aliases) loadDefaults() {
|
|||
|
||||
// Load K9s aliases.
|
||||
func (a Aliases) Load() error {
|
||||
a.loadDefaults()
|
||||
return a.LoadAliases(K9sAlias)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ func TestAliasesLoad(t *testing.T) {
|
|||
a := config.NewAliases()
|
||||
|
||||
assert.Nil(t, a.LoadAliases("test_assets/alias.yml"))
|
||||
assert.Equal(t, 27, len(a.Alias))
|
||||
assert.Equal(t, 2, len(a.Alias))
|
||||
}
|
||||
|
||||
func TestAliasesSave(t *testing.T) {
|
||||
|
|
@ -83,5 +83,5 @@ func TestAliasesSave(t *testing.T) {
|
|||
|
||||
assert.Nil(t, a.SaveAliases("/tmp/a.yml"))
|
||||
assert.Nil(t, a.LoadAliases("/tmp/a.yml"))
|
||||
assert.Equal(t, 28, len(a.Alias))
|
||||
assert.Equal(t, 2, len(a.Alias))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,64 @@
|
|||
package dao
|
||||
|
||||
// Alias represents an alias resource.
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
)
|
||||
|
||||
// Alias tracks standard and custom command aliases.
|
||||
type Alias struct {
|
||||
Generic
|
||||
config.Aliases
|
||||
factory Factory
|
||||
}
|
||||
|
||||
var _ Accessor = &Alias{}
|
||||
// NewAlias returns a new set of aliases.
|
||||
func NewAlias(f Factory) *Alias {
|
||||
return &Alias{
|
||||
Aliases: config.NewAliases(),
|
||||
factory: f,
|
||||
}
|
||||
}
|
||||
|
||||
// ClearAliases remove all aliases.
|
||||
func (a *Alias) Clear() {
|
||||
for k := range a.Alias {
|
||||
delete(a.Alias, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure makes sure alias are loaded.
|
||||
func (a *Alias) Ensure() (config.Alias, error) {
|
||||
if len(a.Alias) == 0 {
|
||||
if err := LoadResources(a.factory); err != nil {
|
||||
return config.Alias{}, err
|
||||
}
|
||||
return a.Alias, a.load()
|
||||
}
|
||||
return a.Alias, nil
|
||||
}
|
||||
|
||||
func (a *Alias) load() error {
|
||||
if err := a.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, gvr := range AllGVRs() {
|
||||
meta, err := MetaFor(gvr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := a.Alias[meta.Kind]; ok {
|
||||
continue
|
||||
}
|
||||
a.Define(string(gvr), strings.ToLower(meta.Kind), meta.Name)
|
||||
if meta.SingularName != "" {
|
||||
a.Define(string(gvr), meta.SingularName)
|
||||
}
|
||||
if meta.ShortNames != nil {
|
||||
a.Define(string(gvr), meta.ShortNames...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package dao
|
|||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Benchmark represents a benchmark resource.
|
||||
|
|
@ -16,6 +14,5 @@ var _ Nuker = &Benchmark{}
|
|||
|
||||
// Delete a Benchmark.
|
||||
func (d *Benchmark) Delete(path string, cascade, force bool) error {
|
||||
log.Debug().Msgf("Benchmark DELETE %q", path)
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
|
|
@ -29,7 +28,7 @@ func (c *Context) Get(_, n string) (runtime.Object, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NamedContext{Name: n, Context: ctx}, nil
|
||||
return &render.NamedContext{Name: n, Context: ctx}, nil
|
||||
}
|
||||
|
||||
// List all Contexts on the current cluster.
|
||||
|
|
@ -40,7 +39,7 @@ func (c *Context) List(string, metav1.ListOptions) ([]runtime.Object, error) {
|
|||
}
|
||||
cc := make([]runtime.Object, 0, len(ctxs))
|
||||
for k, v := range ctxs {
|
||||
cc = append(cc, NewNamedContext(c.config(), k, v))
|
||||
cc = append(cc, render.NewNamedContext(c.config(), k, v))
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
|
|
@ -90,33 +89,33 @@ func (c *Context) KubeUpdate(n string) error {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// NamedContext represents a named cluster context.
|
||||
type NamedContext struct {
|
||||
Name string
|
||||
Context *api.Context
|
||||
config *client.Config
|
||||
}
|
||||
// // NamedContext represents a named cluster context.
|
||||
// type NamedContext struct {
|
||||
// Name string
|
||||
// Context *api.Context
|
||||
// config *client.Config
|
||||
// }
|
||||
|
||||
// NewNamedContext returns a new named context.
|
||||
func NewNamedContext(c *client.Config, n string, ctx *api.Context) *NamedContext {
|
||||
return &NamedContext{Name: n, Context: ctx, config: c}
|
||||
}
|
||||
// // NewNamedContext returns a new named context.
|
||||
// func NewNamedContext(c *client.Config, n string, ctx *api.Context) *NamedContext {
|
||||
// return &NamedContext{Name: n, Context: ctx, config: c}
|
||||
// }
|
||||
|
||||
// MustCurrentContextName return the active context name.
|
||||
func (c *NamedContext) MustCurrentContextName() string {
|
||||
cl, err := c.config.CurrentContextName()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Fetching current context")
|
||||
}
|
||||
return cl
|
||||
}
|
||||
// // MustCurrentContextName return the active context name.
|
||||
// func (c *NamedContext) MustCurrentContextName() string {
|
||||
// cl, err := c.config.CurrentContextName()
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("Fetching current context")
|
||||
// }
|
||||
// return cl
|
||||
// }
|
||||
|
||||
// GetObjectKind returns a schema object.
|
||||
func (c *NamedContext) GetObjectKind() schema.ObjectKind {
|
||||
return nil
|
||||
}
|
||||
// // GetObjectKind returns a schema object.
|
||||
// func (c *NamedContext) GetObjectKind() schema.ObjectKind {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// DeepCopyObject returns a container copy.
|
||||
func (c *NamedContext) DeepCopyObject() runtime.Object {
|
||||
return c
|
||||
}
|
||||
// // DeepCopyObject returns a container copy.
|
||||
// func (c *NamedContext) DeepCopyObject() runtime.Object {
|
||||
// return c
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -62,7 +61,6 @@ func (d *Deployment) Restart(path string) error {
|
|||
|
||||
// Logs tail logs for all pods represented by this Deployment.
|
||||
func (d *Deployment) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing Deployment %q", opts.Path)
|
||||
o, err := d.Get(string(d.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ func (d *DaemonSet) Restart(path string) error {
|
|||
|
||||
// Logs tail logs for all pods represented by this DaemonSet.
|
||||
func (d *DaemonSet) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing DaemonSet %q", opts.Path)
|
||||
o, err := d.Get("apps/v1/daemonsets", opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -112,6 +111,7 @@ func podLogs(ctx context.Context, c chan<- string, sel map[string]string, opts L
|
|||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func toSelector(m map[string]string) string {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dao
|
|||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
|
@ -25,7 +24,6 @@ func (g *Generic) Delete(path string, cascade, force bool) error {
|
|||
}
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
log.Debug().Msgf("DELETING %q:%q -- %q", ns, n, path)
|
||||
opts := metav1.DeleteOptions{PropagationPolicy: &p}
|
||||
if ns != "-" {
|
||||
return g.dynClient().Namespace(ns).Delete(n, &opts)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -21,7 +20,6 @@ var _ Loggable = &Job{}
|
|||
|
||||
// Logs tail logs for all pods represented by this Job.
|
||||
func (j *Job) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing Job %#v", opts)
|
||||
o, err := j.Get(string(j.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type PortForward struct {
|
||||
Generic
|
||||
}
|
||||
|
|
@ -13,7 +9,6 @@ var _ Nuker = &PortForward{}
|
|||
|
||||
// Delete a portforward.
|
||||
func (p *PortForward) Delete(path string, cascade, force bool) error {
|
||||
log.Debug().Msgf("PortForward DELETE %q", path)
|
||||
p.Factory.DeleteForwarder(path)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -26,7 +25,6 @@ var resMetas = ResourceMetas{}
|
|||
// Customize here for non resource types or types with metrics or logs.
|
||||
func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
|
||||
m := Accessors{
|
||||
"alias": &Alias{},
|
||||
"contexts": &Context{},
|
||||
"containers": &Container{},
|
||||
"screendumps": &ScreenDump{},
|
||||
|
|
@ -88,7 +86,8 @@ func IsK9sMeta(m metav1.APIResource) bool {
|
|||
}
|
||||
|
||||
// Load hydrates server preferred+CRDs resource metadata.
|
||||
func Load(f *watch.Factory) error {
|
||||
func LoadResources(f Factory) error {
|
||||
log.Debug().Msgf("LOAD RES")
|
||||
resMetas = make(ResourceMetas, 100)
|
||||
if err := loadPreferred(f, resMetas); err != nil {
|
||||
return err
|
||||
|
|
@ -136,12 +135,12 @@ func loadNonResource(m ResourceMetas) error {
|
|||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["rbac"] = metav1.APIResource{
|
||||
Name: "Rbac",
|
||||
Name: "rbacs",
|
||||
Kind: "Rules",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["policy"] = metav1.APIResource{
|
||||
Name: "Policy",
|
||||
Name: "policies",
|
||||
Kind: "Rules",
|
||||
Namespaced: true,
|
||||
Categories: []string{"k9s"},
|
||||
|
|
@ -158,14 +157,14 @@ func loadNonResource(m ResourceMetas) error {
|
|||
}
|
||||
m["groups"] = metav1.APIResource{
|
||||
Name: "groups",
|
||||
Kind: "group",
|
||||
Kind: "Group",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadPreferred(f *watch.Factory, m ResourceMetas) error {
|
||||
func loadPreferred(f Factory, m ResourceMetas) error {
|
||||
discovery, err := f.Client().CachedDiscovery()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -185,7 +184,7 @@ func loadPreferred(f *watch.Factory, m ResourceMetas) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func loadCRDs(f *watch.Factory, m ResourceMetas) error {
|
||||
func loadCRDs(f Factory, m ResourceMetas) error {
|
||||
oo, err := f.List("apiextensions.k8s.io/v1beta1/customresourcedefinitions", "", labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package dao
|
|||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ScreenDump struct {
|
||||
|
|
@ -15,6 +13,5 @@ var _ Nuker = &ScreenDump{}
|
|||
|
||||
// Delete a ScreenDump.
|
||||
func (d *ScreenDump) Delete(path string, cascade, force bool) error {
|
||||
log.Debug().Msgf("ScreenDump DELETE %q", path)
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -62,7 +61,6 @@ func (s *StatefulSet) Restart(path string) error {
|
|||
|
||||
// Logs tail logs for all pods represented by this StatefulSet.
|
||||
func (s *StatefulSet) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing StatefulSet %q", opts.Path)
|
||||
o, err := s.Get(string(s.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -21,7 +20,6 @@ var _ Loggable = &Service{}
|
|||
|
||||
// Logs tail logs for all pods represented by this Service.
|
||||
func (s *Service) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing Service %q", opts.Path)
|
||||
o, err := s.Get(string(s.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
|
@ -18,13 +19,13 @@ type Alias struct {
|
|||
|
||||
// List returns a collection of screen dumps.
|
||||
func (b *Alias) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
aa, ok := ctx.Value(internal.KeyAliases).(config.Alias)
|
||||
a, ok := ctx.Value(internal.KeyAliases).(*dao.Alias)
|
||||
if !ok {
|
||||
return nil, errors.New("no aliases found in context")
|
||||
}
|
||||
|
||||
m := make(config.ShortNames, len(aa))
|
||||
for alias, gvr := range aa {
|
||||
m := make(config.ShortNames, len(a.Alias))
|
||||
for alias, gvr := range a.Alias {
|
||||
if _, ok := m[gvr]; ok {
|
||||
m[gvr] = append(m[gvr], alias)
|
||||
} else {
|
||||
|
|
@ -40,13 +41,3 @@ func (b *Alias) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
|
||||
return oo, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (b *Alias) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
if err := re.Render(o, render.NonResource, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package model_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
)
|
||||
|
||||
func TestAliasList(t *testing.T) {
|
||||
a := model.Alias{}
|
||||
a.Init(render.ClusterScope, "aliases", makeFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyAliases, makeAliases())
|
||||
oo, err := a.List(ctx)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(oo))
|
||||
assert.Equal(t, 2, len(oo[0].(render.AliasRes).Aliases))
|
||||
}
|
||||
|
||||
func TestAliasHydrate(t *testing.T) {
|
||||
a := model.Alias{}
|
||||
a.Init(render.ClusterScope, "aliases", makeFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyAliases, makeAliases())
|
||||
oo, err := a.List(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
rr := make(render.Rows, len(oo))
|
||||
assert.Nil(t, a.Hydrate(oo, rr, render.Alias{}))
|
||||
assert.Equal(t, 2, len(rr))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func makeAliases() *dao.Alias {
|
||||
return &dao.Alias{
|
||||
Aliases: config.Aliases{
|
||||
Alias: config.Alias{
|
||||
"fred": "v1/fred",
|
||||
"f": "v1/fred",
|
||||
"blee": "v1/blee",
|
||||
"b": "v1/blee",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type testFactory struct{}
|
||||
|
||||
var _ model.Factory = testFactory{}
|
||||
|
||||
func (f testFactory) Client() client.Connection {
|
||||
return nil
|
||||
}
|
||||
func (f testFactory) Get(gvr, path string, sel labels.Selector) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f testFactory) List(gvr, ns string, sel labels.Selector) ([]runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f testFactory) ForResource(ns, gvr string) informers.GenericInformer {
|
||||
return nil
|
||||
}
|
||||
func (f testFactory) WaitForCacheSync() {}
|
||||
func (f testFactory) Forwarders() watch.Forwarders {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeFactory() model.Factory {
|
||||
return testFactory{}
|
||||
}
|
||||
|
|
@ -35,13 +35,3 @@ func (b *Benchmark) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
|
||||
return oo, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (b *Benchmark) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
if err := re.Render(o, render.NonResource, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package model_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBenchmarkList(t *testing.T) {
|
||||
a := model.Benchmark{}
|
||||
a.Init(render.ClusterScope, "benchmarks", makeFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyDir, "test_assets/bench")
|
||||
oo, err := a.List(ctx)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(oo))
|
||||
assert.Equal(t, "test_assets/bench/default_fred_1577308050814961000.txt", oo[0].(render.BenchInfo).Path)
|
||||
}
|
||||
|
||||
func TestBenchmarkHydrate(t *testing.T) {
|
||||
a := model.Benchmark{}
|
||||
a.Init(render.ClusterScope, "benchmarks", makeFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyDir, "test_assets/bench")
|
||||
oo, err := a.List(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
rr := make(render.Rows, len(oo))
|
||||
assert.Nil(t, a.Hydrate(oo, rr, render.Benchmark{}))
|
||||
assert.Equal(t, 1, len(rr))
|
||||
assert.Equal(t, "test_assets/bench/default_fred_1577308050814961000.txt", rr[0].ID)
|
||||
assert.Equal(t, render.Fields{
|
||||
"default",
|
||||
"fred",
|
||||
"fail",
|
||||
"816.6403",
|
||||
"0.0122",
|
||||
"0",
|
||||
"0",
|
||||
"default_fred_1577308050814961000.txt",
|
||||
},
|
||||
rr[0].Fields[:len(rr[0].Fields)-1],
|
||||
)
|
||||
}
|
||||
|
|
@ -9,16 +9,12 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
var _ render.ContainerWithMetrics = &ContainerWithMetrics{}
|
||||
|
||||
// Container represents a container model.
|
||||
type Container struct {
|
||||
Resource
|
||||
|
|
@ -46,77 +42,61 @@ func (c *Container) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
return nil, err
|
||||
}
|
||||
c.pod = &po
|
||||
|
||||
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
|
||||
mx := client.NewMetricsServer(c.factory.Client())
|
||||
var pmx *mv1beta1.PodMetrics
|
||||
if c.factory.Client() != nil {
|
||||
var err error
|
||||
pmx, err = mx.FetchPodMetrics(c.namespace, c.pod.Name)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", c.namespace, c.pod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, co := range po.Spec.InitContainers {
|
||||
res = append(res, ContainerRes{co})
|
||||
res = append(res, makeContainerRes(co, po, pmx, true))
|
||||
}
|
||||
for _, co := range po.Spec.Containers {
|
||||
res = append(res, ContainerRes{co})
|
||||
res = append(res, makeContainerRes(co, po, pmx, false))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (c *Container) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
mx := client.NewMetricsServer(c.factory.Client().(client.Connection))
|
||||
mmx, err := mx.FetchPodMetrics(c.namespace, c.pod.Name)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func makeContainerRes(co v1.Container, po v1.Pod, pmx *mv1beta1.PodMetrics, isInit bool) render.ContainerRes {
|
||||
cmx, err := containerMetrics(co.Name, pmx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", c.namespace, c.pod.Name)
|
||||
log.Warn().Err(err).Msgf("Container metrics for %s", co.Name)
|
||||
}
|
||||
|
||||
var index int
|
||||
for _, o := range oo {
|
||||
co, ok := o.(ContainerRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting containerres but got `%T", o)
|
||||
}
|
||||
row, err := renderCoRow(co.Container.Name, coMetricsFor(co.Container, c.pod, mmx, true), re)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr[index] = row
|
||||
index++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderCoRow(n string, pmx *ContainerWithMetrics, re Renderer) (render.Row, error) {
|
||||
var row render.Row
|
||||
if err := re.Render(pmx, n, &row); err != nil {
|
||||
return render.Row{}, err
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func coMetricsFor(co v1.Container, po *v1.Pod, mmx *mv1beta1.PodMetrics, isInit bool) *ContainerWithMetrics {
|
||||
return &ContainerWithMetrics{
|
||||
container: &co,
|
||||
status: getContainerStatus(co.Name, po.Status),
|
||||
metrics: containerMetrics(co.Name, mmx),
|
||||
isInit: isInit,
|
||||
age: po.ObjectMeta.CreationTimestamp,
|
||||
return render.ContainerRes{
|
||||
Container: co,
|
||||
Status: getContainerStatus(co.Name, po.Status),
|
||||
Metrics: cmx,
|
||||
IsInit: isInit,
|
||||
Age: po.ObjectMeta.CreationTimestamp,
|
||||
}
|
||||
}
|
||||
|
||||
func containerMetrics(n string, mx runtime.Object) *mv1beta1.ContainerMetrics {
|
||||
func containerMetrics(n string, mx runtime.Object) (*mv1beta1.ContainerMetrics, error) {
|
||||
pmx, ok := mx.(*mv1beta1.PodMetrics)
|
||||
if !ok {
|
||||
log.Error().Err(fmt.Errorf("expecting podmetrics but got `%T", mx))
|
||||
return nil
|
||||
return nil, fmt.Errorf("expecting podmetrics but got `%T", mx)
|
||||
}
|
||||
if pmx == nil {
|
||||
return nil, fmt.Errorf("no metrics for container %s", n)
|
||||
}
|
||||
for _, m := range pmx.Containers {
|
||||
if m.Name == n {
|
||||
return &m
|
||||
return &m, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
||||
for _, c := range status.ContainerStatuses {
|
||||
if c.Name == co {
|
||||
|
|
@ -132,50 +112,3 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerWithMetrics represents a container and its metrics.
|
||||
type ContainerWithMetrics struct {
|
||||
container *v1.Container
|
||||
status *v1.ContainerStatus
|
||||
metrics *mv1beta1.ContainerMetrics
|
||||
isInit bool
|
||||
age metav1.Time
|
||||
}
|
||||
|
||||
func (c *ContainerWithMetrics) IsInit() bool {
|
||||
return c.isInit
|
||||
}
|
||||
|
||||
func (c *ContainerWithMetrics) Container() *v1.Container {
|
||||
return c.container
|
||||
}
|
||||
|
||||
func (c *ContainerWithMetrics) ContainerStatus() *v1.ContainerStatus {
|
||||
return c.status
|
||||
}
|
||||
|
||||
// Metrics returns the metrics associated with the pod.
|
||||
func (c *ContainerWithMetrics) Metrics() *mv1beta1.ContainerMetrics {
|
||||
return c.metrics
|
||||
}
|
||||
|
||||
func (c *ContainerWithMetrics) Age() metav1.Time {
|
||||
return c.age
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerRes represents a container K8s resource.
|
||||
type ContainerRes struct {
|
||||
v1.Container
|
||||
}
|
||||
|
||||
// GetObjectKind returns a schema object.
|
||||
func (c ContainerRes) GetObjectKind() schema.ObjectKind {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyObject returns a container copy.
|
||||
func (c ContainerRes) DeepCopyObject() runtime.Object {
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
package model_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
)
|
||||
|
||||
func TestContainerList(t *testing.T) {
|
||||
c := model.Container{}
|
||||
c.Init(render.ClusterScope, "containers", makePodFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyPath, "fred/p1")
|
||||
oo, err := c.List(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(oo))
|
||||
}
|
||||
|
||||
func TestContainerHydrate(t *testing.T) {
|
||||
c := model.Container{}
|
||||
c.Init(render.ClusterScope, "containers", makePodFactory())
|
||||
|
||||
ctx := context.WithValue(context.Background(), internal.KeyPath, "fred/p1")
|
||||
oo, err := c.List(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
rr := make(render.Rows, len(oo))
|
||||
assert.Nil(t, c.Hydrate(oo, rr, render.Container{}))
|
||||
assert.Equal(t, 1, len(rr))
|
||||
assert.Equal(t, "fred", rr[0].ID)
|
||||
assert.Equal(t, render.Fields{"fred", "blee", "false", "Running", "false", "0", "off:off", "n/a", "n/a", "n/a", "n/a", ""}, rr[0].Fields[0:len(rr[0].Fields)-1])
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
type podFactory struct{}
|
||||
|
||||
var _ model.Factory = testFactory{}
|
||||
|
||||
func (f podFactory) Client() client.Connection {
|
||||
return nil
|
||||
}
|
||||
func (f podFactory) Get(gvr, path string, sel labels.Selector) (runtime.Object, error) {
|
||||
var m map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(poYaml()), &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unstructured.Unstructured{Object: m}, nil
|
||||
}
|
||||
func (f podFactory) List(gvr, ns string, sel labels.Selector) ([]runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f podFactory) ForResource(ns, gvr string) informers.GenericInformer { return nil }
|
||||
func (f podFactory) WaitForCacheSync() {}
|
||||
func (f podFactory) Forwarders() watch.Forwarders { return nil }
|
||||
|
||||
func makePodFactory() model.Factory {
|
||||
return podFactory{}
|
||||
}
|
||||
|
||||
func poYaml() string {
|
||||
return `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: "2018-12-14T17:36:43Z"
|
||||
labels:
|
||||
blee: duh
|
||||
name: fred
|
||||
namespace: blee
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: fred
|
||||
value: "1"
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: blee
|
||||
image: blee
|
||||
name: fred
|
||||
resources: {}
|
||||
priority: 1
|
||||
priorityClassName: bozo
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /blee
|
||||
type: Directory
|
||||
name: fred
|
||||
status:
|
||||
containerStatuses:
|
||||
- image: ""
|
||||
imageID: ""
|
||||
lastState: {}
|
||||
name: fred
|
||||
ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
running:
|
||||
startedAt: null
|
||||
phase: Running
|
||||
`
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package model
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -27,23 +26,3 @@ func (c *Context) List(_ context.Context) ([]runtime.Object, error) {
|
|||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Hydrate returns nodes as rows.
|
||||
func (n *Context) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
var index int
|
||||
for _, o := range oo {
|
||||
ctx, ok := o.(*render.NamedContext)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting named context but got %T", o)
|
||||
}
|
||||
|
||||
var row render.Row
|
||||
if err := re.Render(ctx, "", &row); err != nil {
|
||||
return err
|
||||
}
|
||||
rr[index] = row
|
||||
index++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -62,15 +61,8 @@ func (c *Job) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
return jj, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (c *Job) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
if err := re.Render(o, c.namespace, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func isControlledBy(cuid, id string) bool {
|
||||
tokens := strings.Split(cuid, "-")
|
||||
|
|
|
|||
|
|
@ -44,22 +44,6 @@ func (c *PortForward) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
return oo, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (c *PortForward) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
res, ok := o.(render.ForwardRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting a forwardres but got %T", o)
|
||||
}
|
||||
|
||||
if err := re.Render(res, render.NonResource, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package dao
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
|
@ -27,16 +26,16 @@ func Reconcile(ctx context.Context, table render.TableData, gvr client.GVR) (ren
|
|||
if !ok {
|
||||
return table, fmt.Errorf("no factory found for %s", gvr)
|
||||
}
|
||||
m, ok := model.Registry[string(gvr)]
|
||||
m, ok := Registry[string(gvr)]
|
||||
if !ok {
|
||||
log.Warn().Msgf("Resource %s not found in registry. Going generic!", gvr)
|
||||
m = model.ResourceMeta{
|
||||
Model: &model.Generic{},
|
||||
m = ResourceMeta{
|
||||
Model: &Generic{},
|
||||
Renderer: &render.Generic{},
|
||||
}
|
||||
}
|
||||
if m.Model == nil {
|
||||
m.Model = &model.Resource{}
|
||||
m.Model = &Resource{}
|
||||
}
|
||||
m.Model.Init(table.Namespace, string(gvr), factory)
|
||||
|
||||
|
|
@ -41,14 +41,10 @@ func (r *Resource) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) err
|
|||
log.Debug().Msgf("HYDRATE elapsed: %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
var index int
|
||||
for _, o := range oo {
|
||||
var row render.Row
|
||||
if err := re.Render(o, r.namespace, &row); err != nil {
|
||||
for i, o := range oo {
|
||||
if err := re.Render(o, r.namespace, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
rr[index] = row
|
||||
index++
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -34,13 +34,3 @@ func (c *ScreenDump) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
|
||||
return oo, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (c *ScreenDump) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
if err := re.Render(o, render.NonResource, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package model
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
|
|
@ -60,21 +59,8 @@ func (s *Subject) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
return oo, nil
|
||||
}
|
||||
|
||||
// Hydrate returns a pod as container rows.
|
||||
func (s *Subject) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
|
||||
for i, o := range oo {
|
||||
res, ok := o.(render.SubjectRef)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting unstructured but got %T", o)
|
||||
}
|
||||
|
||||
if err := re.Render(res, render.AllNamespaces, &rr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func inSubjectRes(oo []runtime.Object, match string) bool {
|
||||
for _, o := range oo {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
Summary:
|
||||
Total: 816.6403 secs
|
||||
Slowest: 0.0000 secs
|
||||
Fastest: 0.0000 secs
|
||||
Average: NaN secs
|
||||
Requests/sec: 0.0122
|
||||
|
||||
|
||||
Response time histogram:
|
||||
|
||||
|
||||
Latency distribution:
|
||||
|
||||
Details (average, fastest, slowest):
|
||||
DNS+dialup: NaN secs, 0.0000 secs, 0.0000 secs
|
||||
DNS-lookup: NaN secs, 0.0000 secs, 0.0000 secs
|
||||
req write: NaN secs, 0.0000 secs, 0.0000 secs
|
||||
resp wait: NaN secs, 0.0000 secs, 0.0000 secs
|
||||
resp read: NaN secs, 0.0000 secs, 0.0000 secs
|
||||
|
||||
Status code distribution:
|
||||
|
||||
Error distribution:
|
||||
[10] Get http://192.168.64.126:30805/: dial tcp 192.168.64.126:30805: connect: operation timed out
|
||||
|
|
@ -31,16 +31,15 @@ func (Alias) Header(ns string) HeaderRow {
|
|||
|
||||
// Render renders a K8s resource to screen.
|
||||
// BOZO!! Pass in a row with pre-alloc fields??
|
||||
func (Alias) Render(o interface{}, gvr string, r *Row) error {
|
||||
func (Alias) Render(o interface{}, ns string, r *Row) error {
|
||||
a, ok := o.(AliasRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected AliasRes, but got %T", o)
|
||||
}
|
||||
_ = a
|
||||
|
||||
r.ID = gvr
|
||||
gvr1 := client.GVR(a.GVR)
|
||||
grp, res := gvr1.ToRAndG()
|
||||
r.ID = a.GVR
|
||||
gvr := client.GVR(a.GVR)
|
||||
res, grp := gvr.ToRAndG()
|
||||
r.Fields = append(r.Fields,
|
||||
res,
|
||||
strings.Join(a.Aliases, ","),
|
||||
|
|
@ -50,6 +49,7 @@ func (Alias) Render(o interface{}, gvr string, r *Row) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
// AliasRes represents an alias resource.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/gdamore/tcell"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
|
|
@ -81,35 +83,33 @@ func (Container) Header(ns string) HeaderRow {
|
|||
|
||||
// Render renders a K8s resource to screen.
|
||||
func (c Container) Render(o interface{}, name string, r *Row) error {
|
||||
oo, ok := o.(ContainerWithMetrics)
|
||||
co, ok := o.(ContainerRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected ContainerWithMetrics, but got %T", o)
|
||||
return fmt.Errorf("Expected ContainerRes, but got %T", o)
|
||||
}
|
||||
|
||||
co, cs := oo.Container(), oo.ContainerStatus()
|
||||
|
||||
cur, perc := gatherMetrics(co, oo.Metrics())
|
||||
cur, perc := gatherMetrics(co)
|
||||
ready, state, restarts := "false", MissingValue, "0"
|
||||
if cs != nil {
|
||||
ready, state, restarts = boolToStr(cs.Ready), toState(cs.State), strconv.Itoa(int(cs.RestartCount))
|
||||
if co.Status != nil {
|
||||
ready, state, restarts = boolToStr(co.Status.Ready), toState(co.Status.State), strconv.Itoa(int(co.Status.RestartCount))
|
||||
}
|
||||
|
||||
r.ID = co.Name
|
||||
r.ID = co.Container.Name
|
||||
r.Fields = make(Fields, 0, len(c.Header(AllNamespaces)))
|
||||
r.Fields = append(r.Fields,
|
||||
co.Name,
|
||||
co.Image,
|
||||
co.Container.Name,
|
||||
co.Container.Image,
|
||||
ready,
|
||||
state,
|
||||
boolToStr(oo.IsInit()),
|
||||
boolToStr(co.IsInit),
|
||||
restarts,
|
||||
probe(co.LivenessProbe)+":"+probe(co.ReadinessProbe),
|
||||
probe(co.Container.LivenessProbe)+":"+probe(co.Container.ReadinessProbe),
|
||||
cur.cpu,
|
||||
cur.mem,
|
||||
perc.cpu,
|
||||
perc.mem,
|
||||
toStrPorts(co.Ports),
|
||||
toAge(oo.Age()),
|
||||
toStrPorts(co.Container.Ports),
|
||||
toAge(co.Age),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
|
@ -118,20 +118,20 @@ func (c Container) Render(o interface{}, name string, r *Row) error {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (c, p metric) {
|
||||
func gatherMetrics(co ContainerRes) (c, p metric) {
|
||||
c, p = noMetric(), noMetric()
|
||||
if mx == nil {
|
||||
if co.Metrics == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cpu := mx.Usage.Cpu().MilliValue()
|
||||
mem := ToMB(mx.Usage.Memory().Value())
|
||||
cpu := co.Metrics.Usage.Cpu().MilliValue()
|
||||
mem := ToMB(co.Metrics.Usage.Memory().Value())
|
||||
c = metric{
|
||||
cpu: ToMillicore(cpu),
|
||||
mem: ToMi(mem),
|
||||
}
|
||||
|
||||
rcpu, rmem := containerResources(*co)
|
||||
rcpu, rmem := containerResources(co.Container)
|
||||
if rcpu != nil {
|
||||
p.cpu = AsPerc(toPerc(float64(cpu), float64(rcpu.MilliValue())))
|
||||
}
|
||||
|
|
@ -183,3 +183,22 @@ func probe(p *v1.Probe) string {
|
|||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
// ContainerRes represents a container and its metrics.
|
||||
type ContainerRes struct {
|
||||
Container v1.Container
|
||||
Status *v1.ContainerStatus
|
||||
Metrics *mv1beta1.ContainerMetrics
|
||||
IsInit bool
|
||||
Age metav1.Time
|
||||
}
|
||||
|
||||
// GetObjectKind returns a schema object.
|
||||
func (c ContainerRes) GetObjectKind() schema.ObjectKind {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyObject returns a container copy.
|
||||
func (c ContainerRes) DeepCopyObject() runtime.Object {
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,15 @@ import (
|
|||
func TestContainer(t *testing.T) {
|
||||
var c render.Container
|
||||
|
||||
var cm coMX
|
||||
cres := render.ContainerRes{
|
||||
Container: makeContainer(),
|
||||
Status: makeContainerStatus(),
|
||||
Metrics: makeContainerMetrics(),
|
||||
IsInit: false,
|
||||
Age: makeAge(),
|
||||
}
|
||||
var r render.Row
|
||||
assert.Nil(t, c.Render(cm, "blee", &r))
|
||||
assert.Nil(t, c.Render(cres, "blee", &r))
|
||||
assert.Equal(t, "fred", r.ID)
|
||||
assert.Equal(t, render.Fields{
|
||||
"fred",
|
||||
|
|
@ -47,19 +53,7 @@ func toQty(s string) resource.Quantity {
|
|||
|
||||
}
|
||||
|
||||
type coMX struct{}
|
||||
|
||||
var _ render.ContainerWithMetrics = coMX{}
|
||||
|
||||
func (c coMX) Container() *v1.Container {
|
||||
return makeContainer()
|
||||
}
|
||||
|
||||
func (c coMX) ContainerStatus() *v1.ContainerStatus {
|
||||
return makeContainerStatus()
|
||||
}
|
||||
|
||||
func (c coMX) Metrics() *mv1beta1.ContainerMetrics {
|
||||
func makeContainerMetrics() *mv1beta1.ContainerMetrics {
|
||||
return &mv1beta1.ContainerMetrics{
|
||||
Name: "fred",
|
||||
Usage: v1.ResourceList{
|
||||
|
|
@ -69,16 +63,12 @@ func (c coMX) Metrics() *mv1beta1.ContainerMetrics {
|
|||
}
|
||||
}
|
||||
|
||||
func (c coMX) Age() metav1.Time {
|
||||
func makeAge() metav1.Time {
|
||||
return metav1.Time{Time: testTime()}
|
||||
}
|
||||
|
||||
func (c coMX) IsInit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func makeContainer() *v1.Container {
|
||||
return &v1.Container{
|
||||
func makeContainer() v1.Container {
|
||||
return v1.Container{
|
||||
Name: "fred",
|
||||
Image: "img",
|
||||
Resources: v1.ResourceRequirements{
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func NewAlias(gvr client.GVR) ResourceViewer {
|
|||
}
|
||||
|
||||
func (a *Alias) aliasContext(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, internal.KeyAliases, aliases.Alias)
|
||||
return context.WithValue(ctx, internal.KeyAliases, a.App().command.alias)
|
||||
}
|
||||
|
||||
func (a *Alias) bindKeys(aa ui.KeyActions) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ func NewApp(cfg *config.Config) *App {
|
|||
}
|
||||
a.Config = cfg
|
||||
a.InitBench(cfg.K9s.CurrentCluster)
|
||||
a.command = newCommand(&a)
|
||||
|
||||
a.Views()["indicator"] = ui.NewIndicatorView(a.App, a.Styles)
|
||||
a.Views()["clusterInfo"] = newClusterInfoView(&a, client.NewMetricsServer(cfg.GetConnection()))
|
||||
|
|
@ -57,7 +56,6 @@ func (a *App) ActiveView() model.Component {
|
|||
}
|
||||
|
||||
func (a *App) PrevCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
log.Debug().Msgf("PREVIOUS!!!")
|
||||
a.Content.DumpStack()
|
||||
a.Content.DumpPages()
|
||||
if !a.Content.IsLast() {
|
||||
|
|
@ -92,6 +90,11 @@ func (a *App) Init(version string, rate int) error {
|
|||
a.factory = watch.NewFactory(a.Conn())
|
||||
a.initFactory(ns)
|
||||
|
||||
a.command = newCommand(a)
|
||||
if err := a.command.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.clusterInfo().init(version)
|
||||
if a.Config.K9s.GetHeadless() {
|
||||
a.refreshIndicator()
|
||||
|
|
@ -107,10 +110,6 @@ func (a *App) Init(version string, rate int) error {
|
|||
a.Main.AddPage("splash", ui.NewSplash(a.Styles, version), true, true)
|
||||
a.toggleHeader(!a.Config.K9s.GetHeadless())
|
||||
|
||||
if err := a.command.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -266,6 +265,9 @@ func (a *App) switchCtx(name string, loadPods bool) error {
|
|||
}
|
||||
a.initFactory(ns)
|
||||
|
||||
if err := a.command.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.Config.Reset()
|
||||
if err := a.Config.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("Config save failed!")
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/derailed/k9s/internal/ui/dialog"
|
||||
|
|
@ -392,7 +393,7 @@ func (b *Browser) refresh() {
|
|||
if path, ok := ctx.Value(internal.KeyPath).(string); ok && path != "" {
|
||||
b.Path = path
|
||||
}
|
||||
data, err := dao.Reconcile(ctx, b.Table.Data, b.gvr)
|
||||
data, err := model.Reconcile(ctx, b.Table.Data, b.gvr)
|
||||
b.app.QueueUpdateDraw(func() {
|
||||
if err != nil {
|
||||
b.app.Flash().Err(err)
|
||||
|
|
|
|||
|
|
@ -15,17 +15,20 @@ var customViewers MetaViewers
|
|||
|
||||
type command struct {
|
||||
app *App
|
||||
|
||||
alias *dao.Alias
|
||||
}
|
||||
|
||||
func newCommand(app *App) *command {
|
||||
return &command{app: app}
|
||||
return &command{
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *command) Init() error {
|
||||
if err := dao.Load(c.app.factory); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := loadAliases(); err != nil {
|
||||
log.Debug().Msgf("COMMAND INIT")
|
||||
c.alias = dao.NewAlias(c.app.factory)
|
||||
if _, err := c.alias.Ensure(); err != nil {
|
||||
return err
|
||||
}
|
||||
customViewers = loadCustomViewers()
|
||||
|
|
@ -33,6 +36,16 @@ func (c *command) Init() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Reset resets command and reload aliases.
|
||||
func (c *command) Reset() error {
|
||||
c.alias.Clear()
|
||||
if _, err := c.alias.Ensure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) defaultCmd() error {
|
||||
return c.run(c.app.Config.ActiveView())
|
||||
}
|
||||
|
|
@ -68,7 +81,7 @@ func (c *command) isK9sCmd(cmd string) bool {
|
|||
}
|
||||
|
||||
func (c *command) viewMetaFor(cmd string) (string, *MetaViewer, error) {
|
||||
gvr, ok := aliases.Get(cmd)
|
||||
gvr, ok := c.alias.Get(cmd)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("Huh? `%s` command not found", cmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
group = "Group"
|
||||
user = "User"
|
||||
sa = "ServiceAccount"
|
||||
allVerbs = "*"
|
||||
group = "Group"
|
||||
user = "User"
|
||||
sa = "ServiceAccount"
|
||||
)
|
||||
|
||||
// Policy presents a RBAC rules viewer.
|
||||
// Policy presents a RBAC rules viewer based on what a given user/group or sa can do.
|
||||
type Policy struct {
|
||||
ResourceViewer
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,7 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var aliases = config.NewAliases()
|
||||
|
||||
func ToResource(o *unstructured.Unstructured, obj interface{}) error {
|
||||
return runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, &obj)
|
||||
}
|
||||
|
||||
func loadAliases() error {
|
||||
if err := aliases.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, gvr := range dao.AllGVRs() {
|
||||
meta, err := dao.MetaFor(gvr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := aliases.Alias[meta.Kind]; ok {
|
||||
continue
|
||||
}
|
||||
aliases.Define(string(gvr), strings.ToLower(meta.Kind), meta.Name)
|
||||
if meta.SingularName != "" {
|
||||
aliases.Define(string(gvr), meta.SingularName)
|
||||
}
|
||||
if meta.ShortNames != nil {
|
||||
aliases.Define(string(gvr), meta.ShortNames...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCustomViewers() MetaViewers {
|
||||
m := make(MetaViewers, 30)
|
||||
|
||||
coreRes(m)
|
||||
miscRes(m)
|
||||
appsRes(m)
|
||||
|
|
@ -88,12 +48,6 @@ func miscRes(vv MetaViewers) {
|
|||
vv["aliases"] = MetaViewer{
|
||||
viewerFn: NewAlias,
|
||||
}
|
||||
vv["users"] = MetaViewer{
|
||||
viewerFn: NewUser,
|
||||
}
|
||||
vv["groups"] = MetaViewer{
|
||||
viewerFn: NewGroup,
|
||||
}
|
||||
}
|
||||
|
||||
func appsRes(vv MetaViewers) {
|
||||
|
|
@ -118,6 +72,12 @@ func rbacRes(vv MetaViewers) {
|
|||
vv["rbac"] = MetaViewer{
|
||||
enterFn: showRules,
|
||||
}
|
||||
vv["users"] = MetaViewer{
|
||||
viewerFn: NewUser,
|
||||
}
|
||||
vv["groups"] = MetaViewer{
|
||||
viewerFn: NewGroup,
|
||||
}
|
||||
vv["rbac.authorization.k8s.io/v1/clusterroles"] = MetaViewer{
|
||||
enterFn: showRules,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,30 +41,6 @@ func NewFactory(client client.Connection) *Factory {
|
|||
}
|
||||
}
|
||||
|
||||
func (f *Factory) Dump() {
|
||||
log.Debug().Msgf("----------- FACTORIES -------------")
|
||||
for ns := range f.factories {
|
||||
log.Debug().Msgf(" Factory for NS %q", ns)
|
||||
}
|
||||
log.Debug().Msgf("-----------------------------------")
|
||||
}
|
||||
|
||||
func (f *Factory) Debug(gvr string) {
|
||||
log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
|
||||
inf := f.factories[allNamespaces].ForResource(toGVR(gvr))
|
||||
for i, k := range inf.Informer().GetStore().ListKeys() {
|
||||
log.Debug().Msgf("%d -- %s", i, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Factory) Show(ns, gvr string) {
|
||||
log.Debug().Msgf("----------- SHOW FACTORIES %q -------------", ns)
|
||||
inf := f.ForResource(ns, gvr)
|
||||
for _, k := range inf.Informer().GetStore().ListKeys() {
|
||||
log.Debug().Msgf(" Key: %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Factory) List(gvr, ns string, sel labels.Selector) ([]runtime.Object, error) {
|
||||
auth, err := f.Client().CanI(ns, gvr, []string{"list"})
|
||||
if err != nil {
|
||||
|
|
@ -237,6 +213,30 @@ func (f *Factory) Client() client.Connection {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func (f *Factory) Dump() {
|
||||
log.Debug().Msgf("----------- FACTORIES -------------")
|
||||
for ns := range f.factories {
|
||||
log.Debug().Msgf(" Factory for NS %q", ns)
|
||||
}
|
||||
log.Debug().Msgf("-----------------------------------")
|
||||
}
|
||||
|
||||
func (f *Factory) Debug(gvr string) {
|
||||
log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
|
||||
inf := f.factories[allNamespaces].ForResource(toGVR(gvr))
|
||||
for i, k := range inf.Informer().GetStore().ListKeys() {
|
||||
log.Debug().Msgf("%d -- %s", i, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Factory) Show(ns, gvr string) {
|
||||
log.Debug().Msgf("----------- SHOW FACTORIES %q -------------", ns)
|
||||
inf := f.ForResource(ns, gvr)
|
||||
for _, k := range inf.Informer().GetStore().ListKeys() {
|
||||
log.Debug().Msgf(" Key: %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
func namespaced(n string) (string, string) {
|
||||
ns, po := path.Split(n)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue