fix #463
parent
a4e52e41c7
commit
48869f9f51
|
|
@ -569,12 +569,12 @@ to make this project a reality!
|
|||
|
||||
## Meet The Core Team!
|
||||
|
||||
* [Gustavo Silva Paiva](https://github.com/paivagustavo)
|
||||
* <img src="assets/mail.png" width="16" height="auto"/> guustavo.paiva@gmail.com
|
||||
* <img src="assets/twitter.png" width="16" height="auto"/> [@paivagustavodev](https://twitter.com/paivagustavodev)
|
||||
* [Fernand Galiana](https://github.com/derailed)
|
||||
* <img src="assets/mail.png" width="16" height="auto"/> fernand@imhotep.io
|
||||
* <img src="assets/twitter.png" width="16" height="auto"/> [@kitesurfer](https://twitter.com/kitesurfer?lang=en)
|
||||
* [Gustavo Silva Paiva](https://github.com/paivagustavo)
|
||||
* <img src="assets/mail.png" width="16" height="auto"/> guustavo.paiva@gmail.com
|
||||
* <img src="assets/twitter.png" width="16" height="auto"/> [@paivagustavodev](https://twitter.com/paivagustavodev)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||
|
||||
# Release v0.10.10
|
||||
|
||||
## Notes
|
||||
|
||||
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better is as ever very much noticed and appreciated!
|
||||
|
||||
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||
|
||||
---
|
||||
|
||||
## Change Logs
|
||||
|
||||
Maintenance release!
|
||||
|
||||
---
|
||||
|
||||
## Resolved Bugs/Features
|
||||
|
||||
* [Issue #463](https://github.com/derailed/k9s/issues/463)
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"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"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -23,6 +24,20 @@ type Generic struct {
|
|||
table *metav1beta1.Table
|
||||
}
|
||||
|
||||
// List returns a collection of node resources.
|
||||
func (g *Generic) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
var opts metav1.GetOptions
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
gvr := client.NewGVR(g.gvr)
|
||||
req := g.factory.Client().DynDialOrDie().Resource(gvr.AsGVR())
|
||||
if ns == "" {
|
||||
return req.Get(n, opts)
|
||||
}
|
||||
|
||||
return req.Namespace(ns).Get(n, opts)
|
||||
}
|
||||
|
||||
// List returns a collection of node resources.
|
||||
func (g *Generic) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
// Ensures the factory is tracking this resource
|
||||
|
|
@ -39,13 +54,19 @@ func (g *Generic) List(ctx context.Context) ([]runtime.Object, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// BOZO!! Need to know if gvr is namespaced or not
|
||||
ns := g.namespace
|
||||
if g.namespace == render.ClusterScope {
|
||||
ns = render.AllNamespaces
|
||||
}
|
||||
|
||||
log.Debug().Msgf("GENERIC LIST %q:%q", g.namespace, g.gvr)
|
||||
o, err := c.Get().
|
||||
SetHeader("Accept", fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)).
|
||||
Namespace(g.namespace).
|
||||
Resource(gvr.ToR()).
|
||||
VersionedParams(&metav1beta1.TableOptions{}, codec).
|
||||
Do().Get()
|
||||
Namespace(ns).
|
||||
Do().
|
||||
Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ func (r *Resource) Init(ns, gvr string, f dao.Factory) {
|
|||
r.namespace, r.gvr, r.factory = ns, gvr, f
|
||||
}
|
||||
|
||||
// Get returns a resource instance if found, else an error.
|
||||
func (r *Resource) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
return r.factory.Get(r.gvr, path, true, labels.Everything())
|
||||
}
|
||||
|
||||
// List returns a collection of nodes.
|
||||
func (r *Resource) List(ctx context.Context) ([]runtime.Object, error) {
|
||||
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,18 @@ func (t *Table) Watch(ctx context.Context) {
|
|||
go t.updater(ctx)
|
||||
}
|
||||
|
||||
// Get returns a resource instance if found, else an error.
|
||||
func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
meta := t.resourceMeta()
|
||||
factory, ok := ctx.Value(internal.KeyFactory).(dao.Factory)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected Factory in context but got %T", ctx.Value(internal.KeyFactory))
|
||||
}
|
||||
meta.Model.Init(t.namespace, t.gvr, factory)
|
||||
|
||||
return meta.Model.Get(ctx, path)
|
||||
}
|
||||
|
||||
// Refresh update the model now.
|
||||
func (t *Table) Refresh(ctx context.Context) {
|
||||
t.refresh(ctx)
|
||||
|
|
@ -158,11 +170,7 @@ func (t *Table) list(ctx context.Context, l Lister) ([]runtime.Object, error) {
|
|||
return l.List(ctx)
|
||||
}
|
||||
|
||||
func (t *Table) reconcile(ctx context.Context) error {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("RECONCILE elapsed %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
func (t *Table) resourceMeta() ResourceMeta {
|
||||
meta, ok := Registry[t.gvr]
|
||||
if !ok {
|
||||
log.Debug().Msgf("Resource %s not found in registry. Going generic!", t.gvr)
|
||||
|
|
@ -175,6 +183,15 @@ func (t *Table) reconcile(ctx context.Context) error {
|
|||
meta.Model = &Resource{}
|
||||
}
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
func (t *Table) reconcile(ctx context.Context) error {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("RECONCILE elapsed %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
meta := t.resourceMeta()
|
||||
oo, err := t.list(ctx, meta.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ type Lister interface {
|
|||
// List returns a collection of resources.
|
||||
List(context.Context) ([]runtime.Object, error)
|
||||
|
||||
// Get returns a resource instance.
|
||||
Get(ctx context.Context, path string) (runtime.Object, error)
|
||||
|
||||
// Hydrate converts resource rows into tabular data.
|
||||
Hydrate(oo []runtime.Object, rr render.Rows, r Renderer) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ import (
|
|||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
)
|
||||
|
||||
const ageTableCol = "Age"
|
||||
|
||||
// Generic renders a generic resource to screen.
|
||||
type Generic struct {
|
||||
table *metav1beta1.Table
|
||||
|
||||
ageIndex int
|
||||
}
|
||||
|
||||
// SetTable sets the tabular resource.
|
||||
|
|
@ -34,9 +38,16 @@ func (g *Generic) Header(ns string) HeaderRow {
|
|||
if ns == "" {
|
||||
h = append(h, Header{Name: "NAMESPACE"})
|
||||
}
|
||||
for _, c := range g.table.ColumnDefinitions {
|
||||
for i, c := range g.table.ColumnDefinitions {
|
||||
if c.Name == ageTableCol {
|
||||
g.ageIndex = i
|
||||
continue
|
||||
}
|
||||
h = append(h, Header{Name: strings.ToUpper(c.Name)})
|
||||
}
|
||||
if g.ageIndex > 0 {
|
||||
h = append(h, Header{Name: "AGE"})
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
|
@ -48,24 +59,35 @@ func (g *Generic) Render(o interface{}, ns string, r *Row) error {
|
|||
return fmt.Errorf("expecting a TableRow but got %T", o)
|
||||
}
|
||||
|
||||
r.ID, ok = row.Cells[0].(string)
|
||||
var nns = AllNamespaces
|
||||
if ns != ClusterScope {
|
||||
var err error
|
||||
nns, err = extractNamespace(row.Object.Raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
n, ok := row.Cells[0].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("expecting row id to be a string but got %#v", row.Cells[0])
|
||||
}
|
||||
|
||||
rns, err := extractNamespace(row.Object.Raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.ID = FQN(rns, r.ID)
|
||||
r.ID = FQN(nns, n)
|
||||
r.Fields = make(Fields, 0, len(g.Header(ns)))
|
||||
if isAllNamespace(ns) {
|
||||
r.Fields = append(r.Fields, rns)
|
||||
r.Fields = append(r.Fields, nns)
|
||||
}
|
||||
for _, c := range row.Cells {
|
||||
var ageCell interface{}
|
||||
for i, c := range row.Cells {
|
||||
if g.ageIndex > 0 && i == g.ageIndex {
|
||||
ageCell = c
|
||||
continue
|
||||
}
|
||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", c))
|
||||
}
|
||||
if ageCell != nil {
|
||||
r.Fields = append(r.Fields, fmt.Sprintf("%v", ageCell))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,83 @@ import (
|
|||
)
|
||||
|
||||
func TestGenericRender(t *testing.T) {
|
||||
var g render.Generic
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
table *metav1beta1.Table
|
||||
eID string
|
||||
eFields render.Fields
|
||||
eHeader render.HeaderRow
|
||||
}{
|
||||
"specific_ns": {
|
||||
ns: "blee",
|
||||
table: makeNSGeneric(),
|
||||
eID: "ns1/c1",
|
||||
eFields: render.Fields{"c1", "c2", "c3"},
|
||||
eHeader: render.HeaderRow{
|
||||
render.Header{Name: "A"},
|
||||
render.Header{Name: "B"},
|
||||
render.Header{Name: "C"},
|
||||
},
|
||||
},
|
||||
"all_ns": {
|
||||
ns: "",
|
||||
table: makeAllNSGeneric(),
|
||||
eID: "ns1/c1",
|
||||
eFields: render.Fields{"ns1", "c1", "c2", "c3"},
|
||||
eHeader: render.HeaderRow{
|
||||
render.Header{Name: "NAMESPACE"},
|
||||
render.Header{Name: "A"},
|
||||
render.Header{Name: "B"},
|
||||
render.Header{Name: "C"},
|
||||
},
|
||||
},
|
||||
"cluster": {
|
||||
ns: "-",
|
||||
table: makeClusterGeneric(),
|
||||
eID: "c1",
|
||||
eFields: render.Fields{"c1", "c2", "c3"},
|
||||
eHeader: render.HeaderRow{
|
||||
render.Header{Name: "A"},
|
||||
render.Header{Name: "B"},
|
||||
render.Header{Name: "C"},
|
||||
},
|
||||
},
|
||||
"age": {
|
||||
ns: "-",
|
||||
table: makeAgeGeneric(),
|
||||
eID: "c1",
|
||||
eFields: render.Fields{"c1", "c2", "Age"},
|
||||
eHeader: render.HeaderRow{
|
||||
render.Header{Name: "A"},
|
||||
render.Header{Name: "C"},
|
||||
render.Header{Name: "AGE"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var r render.Row
|
||||
row := makeGeneric().Rows[0]
|
||||
assert.Nil(t, g.Render(&row, "blee", &r))
|
||||
|
||||
assert.Equal(t, "blee/a", r.ID)
|
||||
assert.Equal(t, render.Fields{"a", "b", "c"}, r.Fields)
|
||||
var re render.Generic
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
var r render.Row
|
||||
re.SetTable(u.table)
|
||||
assert.Nil(t, re.Render(&u.table.Rows[0], u.ns, &r))
|
||||
assert.Equal(t, u.eID, r.ID)
|
||||
assert.Equal(t, u.eFields, r.Fields)
|
||||
assert.Equal(t, u.eHeader, re.Header(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func makeGeneric() *metav1beta1.Table {
|
||||
func makeNSGeneric() *metav1beta1.Table {
|
||||
return &metav1beta1.Table{
|
||||
ColumnDefinitions: []metav1beta1.TableColumnDefinition{
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
{Name: "C"},
|
||||
{Name: "a"},
|
||||
{Name: "b"},
|
||||
{Name: "c"},
|
||||
},
|
||||
Rows: []metav1beta1.TableRow{
|
||||
{
|
||||
|
|
@ -36,14 +95,96 @@ func makeGeneric() *metav1beta1.Table {
|
|||
"kind": "fred",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"namespace": "blee",
|
||||
"namespace": "ns1",
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"c1",
|
||||
"c2",
|
||||
"c3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeAllNSGeneric() *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": "ns1",
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"c1",
|
||||
"c2",
|
||||
"c3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeClusterGeneric() *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": {
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"c1",
|
||||
"c2",
|
||||
"c3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeAgeGeneric() *metav1beta1.Table {
|
||||
return &metav1beta1.Table{
|
||||
ColumnDefinitions: []metav1beta1.TableColumnDefinition{
|
||||
{Name: "a"},
|
||||
{Name: "Age"},
|
||||
{Name: "c"},
|
||||
},
|
||||
Rows: []metav1beta1.TableRow{
|
||||
{
|
||||
Object: runtime.RawExtension{
|
||||
Raw: []byte(`{
|
||||
"kind": "fred",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "fred"
|
||||
}}`),
|
||||
},
|
||||
Cells: []interface{}{
|
||||
"c1",
|
||||
"Age",
|
||||
"c2",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,50 +1,10 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// Namespaceable represents a namespaceable model.
|
||||
type Namespaceable interface {
|
||||
// ClusterWide returns true if the model represents resource in all namespaces.
|
||||
ClusterWide() bool
|
||||
|
||||
// GetNamespace returns the model namespace.
|
||||
GetNamespace() string
|
||||
|
||||
// SetNamespace changes the model namespace.
|
||||
SetNamespace(string)
|
||||
|
||||
// InNamespace check if current namespace matches models.
|
||||
InNamespace(string) bool
|
||||
}
|
||||
|
||||
// Tabular represents a tabular model.
|
||||
type Tabular interface {
|
||||
Namespaceable
|
||||
|
||||
// Empty returns true if model has no data.
|
||||
Empty() bool
|
||||
|
||||
// Peek returns current model data.
|
||||
Peek() render.TableData
|
||||
|
||||
// Watch watches a given resource for changes.
|
||||
Watch(context.Context)
|
||||
|
||||
// SetRefreshRate sets the model watch loop rate.
|
||||
SetRefreshRate(time.Duration)
|
||||
|
||||
// AddListener registers a model listener.
|
||||
AddListener(model.TableListener)
|
||||
}
|
||||
|
||||
// SelectTable represents a table with selections.
|
||||
type SelectTable struct {
|
||||
*tview.Table
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestTableNew(t *testing.T) {
|
||||
|
|
@ -66,8 +67,11 @@ func (t *testModel) GetNamespace() string { return "blee" }
|
|||
func (t *testModel) SetNamespace(string) {}
|
||||
func (t *testModel) AddListener(model.TableListener) {}
|
||||
func (t *testModel) Watch(context.Context) {}
|
||||
func (t *testModel) InNamespace(string) bool { return true }
|
||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||
func (t *testModel) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (t *testModel) InNamespace(string) bool { return true }
|
||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||
|
||||
func makeTableData() render.TableData {
|
||||
t := render.NewTableData()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package ui
|
||||
|
||||
import "github.com/derailed/k9s/internal/render"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/model"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type (
|
||||
// SortFn represent a function that can sort columnar data.
|
||||
|
|
@ -13,3 +20,41 @@ type (
|
|||
asc bool
|
||||
}
|
||||
)
|
||||
|
||||
// Namespaceable represents a namespaceable model.
|
||||
type Namespaceable interface {
|
||||
// ClusterWide returns true if the model represents resource in all namespaces.
|
||||
ClusterWide() bool
|
||||
|
||||
// GetNamespace returns the model namespace.
|
||||
GetNamespace() string
|
||||
|
||||
// SetNamespace changes the model namespace.
|
||||
SetNamespace(string)
|
||||
|
||||
// InNamespace check if current namespace matches models.
|
||||
InNamespace(string) bool
|
||||
}
|
||||
|
||||
// Tabular represents a tabular model.
|
||||
type Tabular interface {
|
||||
Namespaceable
|
||||
|
||||
// Empty returns true if model has no data.
|
||||
Empty() bool
|
||||
|
||||
// Peek returns current model data.
|
||||
Peek() render.TableData
|
||||
|
||||
// Watch watches a given resource for changes.
|
||||
Watch(context.Context)
|
||||
|
||||
// SetRefreshRate sets the model watch loop rate.
|
||||
SetRefreshRate(time.Duration)
|
||||
|
||||
// AddListener registers a model listener.
|
||||
AddListener(model.TableListener)
|
||||
|
||||
// Get returns a resource instance.
|
||||
Get(ctx context.Context, path string) (runtime.Object, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/gdamore/tcell"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestAliasNew(t *testing.T) {
|
||||
|
|
@ -105,8 +106,11 @@ func (t *testModel) GetNamespace() string { return "blee" }
|
|||
func (t *testModel) SetNamespace(string) {}
|
||||
func (t *testModel) AddListener(model.TableListener) {}
|
||||
func (t *testModel) Watch(context.Context) {}
|
||||
func (t *testModel) InNamespace(string) bool { return true }
|
||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||
func (t *testModel) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (t *testModel) InNamespace(string) bool { return true }
|
||||
func (t *testModel) SetRefreshRate(time.Duration) {}
|
||||
|
||||
func makeTableData() render.TableData {
|
||||
return render.TableData{
|
||||
|
|
|
|||
|
|
@ -151,6 +151,33 @@ func (b *Browser) TableLoadFailed(err error) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Actions...
|
||||
|
||||
func (b *Browser) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := b.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
|
||||
ctx := b.defaultContext()
|
||||
o, err := b.GetModel().Get(ctx, path)
|
||||
if err != nil {
|
||||
b.App().Flash().Errf("unable to get resource %q -- %s", b.gvr, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
raw, err := toYAML(o)
|
||||
if err != nil {
|
||||
b.App().Flash().Errf("unable to marshal resource %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
details := NewDetails(b.app, "YAML", path).Update(raw)
|
||||
if err := b.App().inject(details); err != nil {
|
||||
b.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Browser) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !b.SearchBuff().InCmdMode() {
|
||||
b.SearchBuff().Reset()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
// Table represents a table viewer.
|
||||
|
|
@ -128,32 +127,6 @@ func (t *Table) bindKeys() {
|
|||
})
|
||||
}
|
||||
|
||||
func (t *Table) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
|
||||
o, err := t.app.factory.Get(t.GVR(), path, true, labels.Everything())
|
||||
if err != nil {
|
||||
t.app.Flash().Errf("Unable to get resource %q -- %s", t.gvr, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
raw, err := toYAML(o)
|
||||
if err != nil {
|
||||
t.app.Flash().Errf("Unable to marshal resource %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
details := NewDetails(t.app, "YAML", path).Update(raw)
|
||||
if err := t.app.inject(details); err != nil {
|
||||
t.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/derailed/tview"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestTableSave(t *testing.T) {
|
||||
|
|
@ -97,8 +98,11 @@ func (t *testTableModel) GetNamespace() string { return "blee" }
|
|||
func (t *testTableModel) SetNamespace(string) {}
|
||||
func (t *testTableModel) AddListener(model.TableListener) {}
|
||||
func (t *testTableModel) Watch(context.Context) {}
|
||||
func (t *testTableModel) InNamespace(string) bool { return true }
|
||||
func (t *testTableModel) SetRefreshRate(time.Duration) {}
|
||||
func (t *testTableModel) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (t *testTableModel) InNamespace(string) bool { return true }
|
||||
func (t *testTableModel) SetRefreshRate(time.Duration) {}
|
||||
|
||||
func makeTableData() render.TableData {
|
||||
t := render.NewTableData()
|
||||
|
|
|
|||
|
|
@ -62,35 +62,36 @@ func (f *Factory) Terminate() {
|
|||
|
||||
// List returns a resource collection.
|
||||
func (f *Factory) List(gvr, ns string, wait bool, sel labels.Selector) ([]runtime.Object, error) {
|
||||
if ns == clusterScope {
|
||||
ns = allNamespaces
|
||||
}
|
||||
log.Debug().Msgf("List %q:%q", ns, gvr)
|
||||
inf, err := f.CanForResource(ns, gvr, []string{"list", "watch"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ns == clusterScope {
|
||||
ns = allNamespaces
|
||||
}
|
||||
|
||||
if wait {
|
||||
f.waitForCacheSync(ns)
|
||||
}
|
||||
|
||||
return inf.Lister().ByNamespace(ns).List(sel)
|
||||
}
|
||||
|
||||
// Get retrieves a given resource.
|
||||
func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
|
||||
ns, n := namespaced(path)
|
||||
if ns == clusterScope {
|
||||
ns = allNamespaces
|
||||
}
|
||||
log.Debug().Msgf("GET %q:%q::%q", ns, gvr, n)
|
||||
inf, err := f.CanForResource(ns, gvr, []string{"get"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ns == clusterScope {
|
||||
ns = allNamespaces
|
||||
}
|
||||
|
||||
if wait {
|
||||
f.waitForCacheSync(ns)
|
||||
}
|
||||
|
||||
return inf.Lister().ByNamespace(ns).Get(n)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,11 @@ func Dump(f *Factory) {
|
|||
// Debug for debug.
|
||||
func Debug(f *Factory, ns string, gvr string) {
|
||||
log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
|
||||
inf := f.factories[ns].ForResource(toGVR(gvr))
|
||||
fac, ok := f.factories[ns]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
inf := fac.ForResource(toGVR(gvr))
|
||||
for i, k := range inf.Informer().GetStore().ListKeys() {
|
||||
log.Debug().Msgf("%d -- %s", i, k)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue