parent
f469070b35
commit
3357b1237b
|
|
@ -0,0 +1,27 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.7.3
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Thank you to all that contributed with flushing out issues with 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 always very much appreciated!
|
||||||
|
|
||||||
|
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Change Logs
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Bugs/Features
|
||||||
|
|
||||||
|
+ [Issue #210](https://github.com/derailed/k9s/issues/210)
|
||||||
|
+ [Issue #209](https://github.com/derailed/k9s/issues/209)
|
||||||
|
+ [Issue #206](https://github.com/derailed/k9s/issues/206) Thank you @carlowouters!!
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<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)
|
||||||
|
|
@ -191,7 +191,7 @@ func newTable() *Table {
|
||||||
func newTableHeader() *TableHeader {
|
func newTableHeader() *TableHeader {
|
||||||
return &TableHeader{
|
return &TableHeader{
|
||||||
FgColor: "white",
|
FgColor: "white",
|
||||||
BgColor: "black",
|
BgColor: "red",
|
||||||
SorterColor: "aqua",
|
SorterColor: "aqua",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,23 +80,6 @@ func (r *Resource) listAll(ns, n string) (runtime.Object, error) {
|
||||||
Do().Get()
|
Do().Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) getOne(ns, n string) (runtime.Object, error) {
|
|
||||||
a := fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)
|
|
||||||
_, codec := r.codecs()
|
|
||||||
|
|
||||||
c, err := r.getClient()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Get().
|
|
||||||
SetHeader("Accept", a).
|
|
||||||
Namespace(ns).
|
|
||||||
Resource(n).
|
|
||||||
VersionedParams(&metav1beta1.TableOptions{}, codec).
|
|
||||||
Do().Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) getClient() (*rest.RESTClient, error) {
|
func (r *Resource) getClient() (*rest.RESTClient, error) {
|
||||||
crConfig := r.RestConfigOrDie()
|
crConfig := r.RestConfigOrDie()
|
||||||
crConfig.GroupVersion = &schema.GroupVersion{Group: r.group, Version: r.version}
|
crConfig.GroupVersion = &schema.GroupVersion{Group: r.group, Version: r.version}
|
||||||
|
|
|
||||||
|
|
@ -287,9 +287,9 @@ func toRes(r v1.ResourceList) (string, string) {
|
||||||
|
|
||||||
func probe(p *v1.Probe) string {
|
func probe(p *v1.Probe) string {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return "on"
|
|
||||||
}
|
|
||||||
return "off"
|
return "off"
|
||||||
|
}
|
||||||
|
return "on"
|
||||||
}
|
}
|
||||||
|
|
||||||
func asMi(v int64) float64 {
|
func asMi(v int64) float64 {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProbe(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
probe *v1.Probe
|
||||||
|
e string
|
||||||
|
}{
|
||||||
|
"defined": {&v1.Probe{}, "on"},
|
||||||
|
"undefined": {nil, "off"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
assert.Equal(t, u.e, probe(u.probe))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsMi(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
mem int64
|
||||||
|
e float64
|
||||||
|
}{
|
||||||
|
"zero": {0, 0},
|
||||||
|
"1Mb": {1024 * 1024, 1.048576e+06},
|
||||||
|
"10Mb": {10 * 1024 * 1024, 1.048576e+07},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
assert.Equal(t, u.e, asMi(u.mem))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToRes(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
res v1.ResourceList
|
||||||
|
ecpu, emem string
|
||||||
|
}{
|
||||||
|
"cool": {v1.ResourceList{
|
||||||
|
v1.ResourceCPU: toQty("10m"),
|
||||||
|
v1.ResourceMemory: toQty("20Mi"),
|
||||||
|
},
|
||||||
|
"10", "20"},
|
||||||
|
"noRes": {v1.ResourceList{},
|
||||||
|
"0", "0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
cpu, mem := toRes(u.res)
|
||||||
|
assert.Equal(t, u.ecpu, cpu)
|
||||||
|
assert.Equal(t, u.emem, mem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToState(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
state v1.ContainerState
|
||||||
|
e string
|
||||||
|
}{
|
||||||
|
"empty": {v1.ContainerState{},
|
||||||
|
MissingValue},
|
||||||
|
"running": {
|
||||||
|
v1.ContainerState{Running: &v1.ContainerStateRunning{}},
|
||||||
|
"Running",
|
||||||
|
},
|
||||||
|
"waiting": {
|
||||||
|
v1.ContainerState{Waiting: &v1.ContainerStateWaiting{}},
|
||||||
|
"Waiting",
|
||||||
|
},
|
||||||
|
"waitingReason": {
|
||||||
|
v1.ContainerState{Waiting: &v1.ContainerStateWaiting{Reason: "blee"}},
|
||||||
|
"blee",
|
||||||
|
},
|
||||||
|
"terminated": {
|
||||||
|
v1.ContainerState{Terminated: &v1.ContainerStateTerminated{}},
|
||||||
|
"Terminated",
|
||||||
|
},
|
||||||
|
"terminatedReason": {
|
||||||
|
v1.ContainerState{Terminated: &v1.ContainerStateTerminated{Reason: "blee"}},
|
||||||
|
"blee",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
assert.Equal(t, u.e, toState(u.state))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Helpers...
|
||||||
|
|
||||||
|
func toQty(s string) resource.Quantity {
|
||||||
|
q, _ := resource.ParseQuantity(s)
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
@ -94,7 +94,7 @@ func (r *Custom) List(ns string) (Columnars, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ii) != 1 {
|
if len(ii) == 0 {
|
||||||
return Columnars{}, errors.New("no resources found")
|
return Columnars{}, errors.New("no resources found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ func newContainerView(t string, app *appView, list resource.List, path string, e
|
||||||
{
|
{
|
||||||
v.path = &path
|
v.path = &path
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.viewLogs
|
||||||
v.colorerFn = containerColorer
|
v.colorerFn = containerColorer
|
||||||
v.current = app.content.GetPrimitive("main").(igniter)
|
v.current = app.content.GetPrimitive("main").(igniter)
|
||||||
v.exitFn = exitFn
|
v.exitFn = exitFn
|
||||||
|
|
@ -45,7 +46,6 @@ func (v *containerView) extraActions(aa keyActions) {
|
||||||
aa[KeyS] = newKeyAction("Shell", v.shellCmd, true)
|
aa[KeyS] = newKeyAction("Shell", v.shellCmd, true)
|
||||||
aa[tcell.KeyEscape] = newKeyAction("Back", v.backCmd, false)
|
aa[tcell.KeyEscape] = newKeyAction("Back", v.backCmd, false)
|
||||||
aa[KeyP] = newKeyAction("Previous", v.backCmd, false)
|
aa[KeyP] = newKeyAction("Previous", v.backCmd, false)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Logs", v.logsCmd, false)
|
|
||||||
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(6, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(6, false), true)
|
||||||
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(7, false), true)
|
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(7, false), true)
|
||||||
aa[KeyAltC] = newKeyAction("Sort CPU%", v.sortColCmd(8, false), true)
|
aa[KeyAltC] = newKeyAction("Sort CPU%", v.sortColCmd(8, false), true)
|
||||||
|
|
@ -72,18 +72,22 @@ func (v *containerView) getSelection() string {
|
||||||
|
|
||||||
// Handlers...
|
// Handlers...
|
||||||
|
|
||||||
|
func (v *containerView) viewLogs(app *appView, _, res, sel string) {
|
||||||
|
cell := v.getTV().GetCell(v.selectedRow, 3)
|
||||||
|
if cell != nil && strings.TrimSpace(cell.Text) != "Running" {
|
||||||
|
v.app.flash().err(errors.New("No logs for a non running container"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v.showLogs(sel, v.list.GetName(), v, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *containerView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *containerView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if !v.rowSelected() {
|
if !v.rowSelected() {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
cell := v.getTV().GetCell(v.selectedRow, 3)
|
v.viewLogs(v.app, v.list.GetNamespace(), v.list.GetName(), v.selectedItem)
|
||||||
if cell != nil && strings.TrimSpace(cell.Text) != "Running" {
|
|
||||||
v.app.flash().err(errors.New("No logs for a non running container"))
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
v.showLogs(v.selectedItem, v.list.GetName(), v, false)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
"github.com/gdamore/tcell"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type contextView struct {
|
type contextView struct {
|
||||||
|
|
@ -14,6 +13,7 @@ type contextView struct {
|
||||||
func newContextView(t string, app *appView, list resource.List) resourceViewer {
|
func newContextView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := contextView{newResourceView(t, app, list).(*resourceView)}
|
v := contextView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.useCtx
|
||||||
v.getTV().cleanseFn = v.cleanser
|
v.getTV().cleanseFn = v.cleanser
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -21,21 +21,14 @@ func newContextView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
|
|
||||||
func (v *contextView) extraActions(aa keyActions) {
|
func (v *contextView) extraActions(aa keyActions) {
|
||||||
delete(v.getTV().actions, KeyShiftA)
|
delete(v.getTV().actions, KeyShiftA)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("Switch", v.useCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *contextView) useCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *contextView) useCtx(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
if err := v.useContext(sel); err != nil {
|
||||||
return evt
|
app.flash().err(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := v.useContext(v.selectedItem); err != nil {
|
app.gotoResource("po", true)
|
||||||
v.app.flash().err(err)
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
|
|
||||||
v.app.gotoResource("po", true)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*contextView) cleanser(s string) string {
|
func (*contextView) cleanser(s string) string {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type deployView struct {
|
||||||
func newDeployView(t string, app *appView, list resource.List) resourceViewer {
|
func newDeployView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := deployView{newResourceView(t, app, list).(*resourceView)}
|
v := deployView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +24,6 @@ func newDeployView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
func (v *deployView) extraActions(aa keyActions) {
|
func (v *deployView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
||||||
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *deployView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *deployView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -36,30 +36,25 @@ func (v *deployView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *deployView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *deployView) showPods(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
ns, n := namespaced(sel)
|
||||||
return evt
|
d := k8s.NewDeployment(app.conn())
|
||||||
}
|
|
||||||
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
d := k8s.NewDeployment(v.app.conn())
|
|
||||||
dep, err := d.Get(ns, n)
|
dep, err := d.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetching Deployment %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetching Deployment %s", sel)
|
||||||
v.app.flash().err(err)
|
app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dp := dep.(*v1.Deployment)
|
dp := dep.(*v1.Deployment)
|
||||||
|
l, err := metav1.LabelSelectorAsSelector(dp.Spec.Selector)
|
||||||
sel, err := metav1.LabelSelectorAsSelector(dp.Spec.Selector)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Converting selector for Deployment %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Converting selector for Deployment %s", sel)
|
||||||
v.app.flash().err(err)
|
app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
showPods(v.app, ns, "Deployment", v.selectedItem, sel.String(), "", v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
showPods(app, ns, "Deployment", sel, l.String(), "", v.backCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *deployView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *deployView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type daemonSetView struct {
|
||||||
func newDaemonSetView(t string, app *appView, list resource.List) resourceViewer {
|
func newDaemonSetView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := daemonSetView{newResourceView(t, app, list).(*resourceView)}
|
v := daemonSetView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +24,6 @@ func newDaemonSetView(t string, app *appView, list resource.List) resourceViewer
|
||||||
func (v *daemonSetView) extraActions(aa keyActions) {
|
func (v *daemonSetView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
||||||
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *daemonSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *daemonSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -36,30 +36,25 @@ func (v *daemonSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *daemonSetView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *daemonSetView) showPods(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
ns, n := namespaced(sel)
|
||||||
return evt
|
d := k8s.NewDaemonSet(app.conn())
|
||||||
}
|
|
||||||
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
d := k8s.NewDaemonSet(v.app.conn())
|
|
||||||
dset, err := d.Get(ns, n)
|
dset, err := d.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetching DeaemonSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetching DeaemonSet %s", sel)
|
||||||
v.app.flash().err(err)
|
v.app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ds := dset.(*extv1beta1.DaemonSet)
|
ds := dset.(*extv1beta1.DaemonSet)
|
||||||
|
l, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector)
|
||||||
sel, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Converting selector for DaemonSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Converting selector for DaemonSet %s", sel)
|
||||||
v.app.flash().err(err)
|
app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
showPods(v.app, ns, "DaemonSet", v.selectedItem, sel.String(), "", v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
showPods(app, ns, "DaemonSet", sel, l.String(), "", v.backCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *daemonSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *daemonSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ func (v *dumpView) registerActions() {
|
||||||
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
||||||
v.actions[tcell.KeyEnter] = newKeyAction("Enter", v.enterCmd, true)
|
v.actions[tcell.KeyEnter] = newKeyAction("Enter", v.enterCmd, true)
|
||||||
v.actions[tcell.KeyCtrlD] = newKeyAction("Delete", v.deleteCmd, true)
|
v.actions[tcell.KeyCtrlD] = newKeyAction("Delete", v.deleteCmd, true)
|
||||||
|
v.actions[tcell.KeyCtrlS] = newKeyAction("Save", v.app.noopCmd, false)
|
||||||
|
|
||||||
vu := v.getTV()
|
vu := v.getTV()
|
||||||
vu.setActions(v.actions)
|
vu.setActions(v.actions)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
|
@ -106,7 +105,6 @@ func TestStripPort(t *testing.T) {
|
||||||
|
|
||||||
for k, u := range uu {
|
for k, u := range uu {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
fmt.Println("TCP?", u.port, isTCPPort(u.port))
|
|
||||||
assert.Equal(t, u.e, stripPort(u.port))
|
assert.Equal(t, u.e, stripPort(u.port))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type jobView struct {
|
||||||
func newJobView(t string, app *appView, list resource.List) resourceViewer {
|
func newJobView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := jobView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
v := jobView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
||||||
|
|
||||||
picker := newSelectList(&v)
|
picker := newSelectList(&v)
|
||||||
|
|
@ -99,33 +100,27 @@ func (v *jobView) showLogs(path, co, view string, parent loggable, prev bool) {
|
||||||
func (v *jobView) extraActions(aa keyActions) {
|
func (v *jobView) extraActions(aa keyActions) {
|
||||||
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
||||||
aa[KeyShiftL] = newKeyAction("Logs Previous", v.prevLogsCmd, true)
|
aa[KeyShiftL] = newKeyAction("Logs Previous", v.prevLogsCmd, true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *jobView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *jobView) showPods(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
ns, n := namespaced(sel)
|
||||||
return evt
|
j := k8s.NewJob(app.conn())
|
||||||
}
|
|
||||||
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
j := k8s.NewJob(v.app.conn())
|
|
||||||
job, err := j.Get(ns, n)
|
job, err := j.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetching Job %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetching Job %s", sel)
|
||||||
v.app.flash().err(err)
|
app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jo := job.(*batchv1.Job)
|
jo := job.(*batchv1.Job)
|
||||||
|
l, err := metav1.LabelSelectorAsSelector(jo.Spec.Selector)
|
||||||
sel, err := metav1.LabelSelectorAsSelector(jo.Spec.Selector)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Converting selector for Job %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Converting selector for Job %s", sel)
|
||||||
v.app.flash().err(err)
|
app.flash().err(err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
showPods(v.app, "", "Job", v.selectedItem, sel.String(), "", v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
showPods(app, "", "Job", sel, l.String(), "", v.backCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *jobView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *jobView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ type nodeView struct {
|
||||||
func newNodeView(t string, app *appView, list resource.List) resourceViewer {
|
func newNodeView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := nodeView{newResourceView(t, app, list).(*resourceView)}
|
v := nodeView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +20,6 @@ func newNodeView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
func (v *nodeView) extraActions(aa keyActions) {
|
func (v *nodeView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(7, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(7, false), true)
|
||||||
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(8, false), true)
|
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(8, false), true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *nodeView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *nodeView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -32,14 +32,8 @@ func (v *nodeView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *nodeView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *nodeView) showPods(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
showPods(app, "", "Node", sel, "", "spec.nodeName="+sel, v.backCmd)
|
||||||
return evt
|
|
||||||
}
|
|
||||||
|
|
||||||
showPods(v.app, "", "Node", v.selectedItem, "", "spec.nodeName="+v.selectedItem, v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *nodeView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *nodeView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -25,24 +25,19 @@ func newNamespaceView(t string, app *appView, list resource.List) resourceViewer
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
v.selectedFn = v.getSelectedItem
|
v.selectedFn = v.getSelectedItem
|
||||||
v.decorateFn = v.decorate
|
v.decorateFn = v.decorate
|
||||||
|
v.enterFn = v.switchNs
|
||||||
v.getTV().cleanseFn = v.cleanser
|
v.getTV().cleanseFn = v.cleanser
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *namespaceView) extraActions(aa keyActions) {
|
func (v *namespaceView) extraActions(aa keyActions) {
|
||||||
aa[tcell.KeyEnter] = newKeyAction("Switch", v.switchNsCmd, true)
|
|
||||||
aa[KeyU] = newKeyAction("Use", v.useNsCmd, true)
|
aa[KeyU] = newKeyAction("Use", v.useNsCmd, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *namespaceView) switchNsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *namespaceView) switchNs(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
v.useNamespace(sel)
|
||||||
return evt
|
app.gotoResource("po", true)
|
||||||
}
|
|
||||||
v.useNamespace(v.getSelectedItem())
|
|
||||||
v.app.gotoResource("po", true)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *namespaceView) useNsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *namespaceView) useNsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ func computeMaxColumns(pads maxyPad, sortCol int, table resource.TableData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var row int
|
var row int
|
||||||
for k, rev := range table.Rows {
|
for _, rev := range table.Rows {
|
||||||
ageIndex := len(rev.Fields) - 1
|
ageIndex := len(rev.Fields) - 1
|
||||||
for index, field := range rev.Fields {
|
for index, field := range rev.Fields {
|
||||||
// Date field comes out as timestamp.
|
// Date field comes out as timestamp.
|
||||||
|
|
@ -31,7 +31,6 @@ func computeMaxColumns(pads maxyPad, sortCol int, table resource.TableData) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
field = duration.HumanDuration(dur)
|
field = duration.HumanDuration(dur)
|
||||||
}
|
}
|
||||||
table.Rows[k].Fields[index] = field
|
|
||||||
}
|
}
|
||||||
width := len(field) + colPadding
|
width := len(field) + colPadding
|
||||||
if width > pads[index] {
|
if width > pads[index] {
|
||||||
|
|
|
||||||
|
|
@ -64,20 +64,17 @@ func (v *podView) extraActions(aa keyActions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *podView) listContainers(app *appView, _, res, sel string) {
|
func (v *podView) listContainers(app *appView, _, res, sel string) {
|
||||||
if !v.rowSelected() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
po, err := v.app.informer.Get(watch.PodIndex, sel, metav1.GetOptions{})
|
po, err := v.app.informer.Get(watch.PodIndex, sel, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Unable to retrieve pod %s", sel)
|
log.Error().Err(err).Msgf("Unable to retrieve pod %s", sel)
|
||||||
app.flash().errf("Unable to retrieve pods %s", err)
|
app.flash().errf("Unable to retrieve pods %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pod := po.(*v1.Pod)
|
pod := po.(*v1.Pod)
|
||||||
mx := k8s.NewMetricsServer(app.conn())
|
mx := k8s.NewMetricsServer(app.conn())
|
||||||
list := resource.NewContainerList(app.conn(), mx, pod)
|
list := resource.NewContainerList(app.conn(), mx, pod)
|
||||||
title := skinTitle(fmt.Sprintf(containerFmt, "Containers", sel), v.app.styles.Style)
|
title := skinTitle(fmt.Sprintf(containerFmt, "Containers", sel), app.styles.Style)
|
||||||
|
|
||||||
// Stop my updater
|
// Stop my updater
|
||||||
if v.cancelFn != nil {
|
if v.cancelFn != nil {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/derailed/tview"
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
type portSelector struct {
|
||||||
|
title, port string
|
||||||
|
ok, cancel func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSelector(title, port string, okFn, cancelFn func()) *portSelector {
|
||||||
|
return &portSelector{
|
||||||
|
title: title,
|
||||||
|
port: port,
|
||||||
|
ok: okFn,
|
||||||
|
cancel: cancelFn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *portSelector) show(app *appView) {
|
||||||
|
f := tview.NewForm()
|
||||||
|
f.SetItemPadding(0)
|
||||||
|
f.SetButtonsAlign(tview.AlignCenter).
|
||||||
|
SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor).
|
||||||
|
SetButtonTextColor(tview.Styles.PrimaryTextColor).
|
||||||
|
SetLabelColor(tcell.ColorAqua).
|
||||||
|
SetFieldTextColor(tcell.ColorOrange)
|
||||||
|
|
||||||
|
f1 := p.port
|
||||||
|
f.AddInputField("Pod Port:", f1, 20, nil, func(changed string) {
|
||||||
|
f1 = changed
|
||||||
|
})
|
||||||
|
|
||||||
|
f.AddButton("OK", p.ok)
|
||||||
|
f.AddButton("Cancel", p.cancel)
|
||||||
|
|
||||||
|
modal := tview.NewModalForm("<"+p.title+">", f)
|
||||||
|
modal.SetDoneFunc(func(_ int, b string) {
|
||||||
|
p.cancel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -185,6 +185,11 @@ func (v *resourceView) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if v.getTV().filterCmd(evt) == nil {
|
if v.getTV().filterCmd(evt) == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.selectedItem == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if v.enterFn != nil {
|
if v.enterFn != nil {
|
||||||
v.enterFn(v.app, v.list.GetNamespace(), v.list.GetName(), v.selectedItem)
|
v.enterFn(v.app, v.list.GetNamespace(), v.list.GetName(), v.selectedItem)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ type replicaSetView struct {
|
||||||
func newReplicaSetView(t string, app *appView, list resource.List) resourceViewer {
|
func newReplicaSetView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := replicaSetView{newResourceView(t, app, list).(*resourceView)}
|
v := replicaSetView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -31,7 +32,6 @@ func (v *replicaSetView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(2, false), true)
|
||||||
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(3, false), true)
|
||||||
aa[tcell.KeyCtrlB] = newKeyAction("Rollback", v.rollbackCmd, true)
|
aa[tcell.KeyCtrlB] = newKeyAction("Rollback", v.rollbackCmd, true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *replicaSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *replicaSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -44,30 +44,24 @@ func (v *replicaSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *replicaSetView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *replicaSetView) showPods(app *appView, ns, res, sel string) {
|
||||||
if !v.rowSelected() {
|
ns, n := namespaced(sel)
|
||||||
return evt
|
rset := k8s.NewReplicaSet(app.conn())
|
||||||
}
|
|
||||||
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
rset := k8s.NewReplicaSet(v.app.conn())
|
|
||||||
r, err := rset.Get(ns, n)
|
r, err := rset.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetching ReplicaSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetching ReplicaSet %s", sel)
|
||||||
v.app.flash().errf("Replicaset failed %s", err)
|
app.flash().errf("Replicaset failed %s", err)
|
||||||
return evt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rs := r.(*v1.ReplicaSet)
|
rs := r.(*v1.ReplicaSet)
|
||||||
|
l, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
|
||||||
sel, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Converting selector for ReplicaSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Converting selector for ReplicaSet %s", sel)
|
||||||
v.app.flash().errf("Selector failed %s", err)
|
app.flash().errf("Selector failed %s", err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
showPods(v.app, "", "ReplicaSet", v.selectedItem, sel.String(), "", v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
showPods(app, "", "ReplicaSet", sel, l.String(), "", v.backCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *replicaSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *replicaSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,9 @@ func less(asc bool, c1, c2 string) bool {
|
||||||
return !b
|
return !b
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDurationSort(asc bool, c1, c2 string) (bool, bool) {
|
func isDurationSort(asc bool, s1, s2 string) (bool, bool) {
|
||||||
d1, ok1 := isDuration(c1)
|
d1, ok1 := isDuration(s1)
|
||||||
d2, ok2 := isDuration(c2)
|
d2, ok2 := isDuration(s2)
|
||||||
if !ok1 || !ok2 {
|
if !ok1 || !ok2 {
|
||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ func isDurationSort(asc bool, c1, c2 string) (bool, bool) {
|
||||||
if asc {
|
if asc {
|
||||||
return d1 <= d2, true
|
return d1 <= d2, true
|
||||||
}
|
}
|
||||||
return d1 > d2, true
|
return d1 >= d2, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isMetricSort(asc bool, c1, c2 string) (bool, bool) {
|
func isMetricSort(asc bool, c1, c2 string) (bool, bool) {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ func TestGroupSort(t *testing.T) {
|
||||||
{true, []string{"b-21", "b-2"}, []string{"b-2", "b-21"}},
|
{true, []string{"b-21", "b-2"}, []string{"b-2", "b-21"}},
|
||||||
{false, []string{"b-21", "b-2"}, []string{"b-21", "b-2"}},
|
{false, []string{"b-21", "b-2"}, []string{"b-21", "b-2"}},
|
||||||
{true, []string{"4m", "3m2s"}, []string{"3m2s", "4m"}},
|
{true, []string{"4m", "3m2s"}, []string{"3m2s", "4m"}},
|
||||||
|
{true, []string{"3y37d", "2y4d"}, []string{"2y4d", "3y37d"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range uu {
|
for _, u := range uu {
|
||||||
|
|
@ -80,3 +81,24 @@ func TestRowSort(t *testing.T) {
|
||||||
assert.Equal(t, u.expect, r.rows)
|
assert.Equal(t, u.expect, r.rows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsDurationSort(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
s1, s2 string
|
||||||
|
asc, e bool
|
||||||
|
}{
|
||||||
|
"ascLess": {"10h5m", "2h10m", true, false},
|
||||||
|
"descGreater": {"10h5m", "2h10m", false, true},
|
||||||
|
"ascEqual": {"2h10m", "2h10m", true, true},
|
||||||
|
"descEqual": {"2h10m", "2h10m", false, true},
|
||||||
|
"ascGreater": {"10h10m", "2h5m", true, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
less, ok := isDurationSort(u.asc, u.s1, u.s2)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, u.e, less)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type statefulSetView struct {
|
||||||
func newStatefulSetView(t string, app *appView, list resource.List) resourceViewer {
|
func newStatefulSetView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := statefulSetView{newResourceView(t, app, list).(*resourceView)}
|
v := statefulSetView{newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +24,6 @@ func newStatefulSetView(t string, app *appView, list resource.List) resourceView
|
||||||
func (v *statefulSetView) extraActions(aa keyActions) {
|
func (v *statefulSetView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(1, false), true)
|
aa[KeyShiftD] = newKeyAction("Sort Desired", v.sortColCmd(1, false), true)
|
||||||
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(2, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort Current", v.sortColCmd(2, false), true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *statefulSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *statefulSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -36,30 +36,25 @@ func (v *statefulSetView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *statefulSetView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *statefulSetView) showPods(app *appView, ns, res, sel string) {
|
||||||
if !v.rowSelected() {
|
ns, n := namespaced(sel)
|
||||||
return evt
|
s := k8s.NewStatefulSet(app.conn())
|
||||||
}
|
st, err := s.Get(ns, n)
|
||||||
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
d := k8s.NewStatefulSet(v.app.conn())
|
|
||||||
s, err := d.Get(ns, n)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetching StatefulSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetching StatefulSet %s", sel)
|
||||||
v.app.flash().errf("Unable to fetch statefulset %s", err)
|
app.flash().errf("Unable to fetch statefulset %s", err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
sts := s.(*v1.StatefulSet)
|
|
||||||
|
|
||||||
sel, err := metav1.LabelSelectorAsSelector(sts.Spec.Selector)
|
sts := st.(*v1.StatefulSet)
|
||||||
|
l, err := metav1.LabelSelectorAsSelector(sts.Spec.Selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Converting selector for StatefulSet %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Converting selector for StatefulSet %s", sel)
|
||||||
v.app.flash().errf("Selector failed %s", err)
|
app.flash().errf("Selector failed %s", err)
|
||||||
return evt
|
return
|
||||||
}
|
}
|
||||||
showPods(v.app, "", "StatefulSet", v.selectedItem, sel.String(), "", v.backCmd)
|
|
||||||
|
|
||||||
return nil
|
showPods(app, "", "StatefulSet", sel, l.String(), "", v.backCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *statefulSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *statefulSetView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ type svcView struct {
|
||||||
func newSvcView(t string, app *appView, list resource.List) resourceViewer {
|
func newSvcView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := svcView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
v := svcView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.showPods
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +30,6 @@ func newSvcView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
func (v *svcView) extraActions(aa keyActions) {
|
func (v *svcView) extraActions(aa keyActions) {
|
||||||
aa[tcell.KeyCtrlB] = newKeyAction("Bench", v.benchCmd, true)
|
aa[tcell.KeyCtrlB] = newKeyAction("Bench", v.benchCmd, true)
|
||||||
aa[KeyAltB] = newKeyAction("Bench Stop", v.benchStopCmd, true)
|
aa[KeyAltB] = newKeyAction("Bench Stop", v.benchStopCmd, true)
|
||||||
aa[tcell.KeyEnter] = newKeyAction("View Pods", v.showPodsCmd, true)
|
|
||||||
|
|
||||||
aa[KeyShiftT] = newKeyAction("Sort Type", v.sortColCmd(1, false), true)
|
aa[KeyShiftT] = newKeyAction("Sort Type", v.sortColCmd(1, false), true)
|
||||||
}
|
}
|
||||||
|
|
@ -44,23 +44,19 @@ func (v *svcView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *svcView) showPodsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *svcView) showPods(app *appView, ns, res, sel string) {
|
||||||
if !v.rowSelected() {
|
s := k8s.NewService(app.conn())
|
||||||
return evt
|
ns, n := namespaced(sel)
|
||||||
}
|
svc, err := s.Get(ns, n)
|
||||||
|
|
||||||
s := k8s.NewService(v.app.conn())
|
|
||||||
ns, n := namespaced(v.selectedItem)
|
|
||||||
res, err := s.Get(ns, n)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Fetch service %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Fetch service %s", sel)
|
||||||
return nil
|
app.flash().err(err)
|
||||||
}
|
return
|
||||||
if svc, ok := res.(*v1.Service); ok {
|
|
||||||
v.showSvcPods(ns, svc.Spec.Selector, v.backCmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if s, ok := svc.(*v1.Service); ok {
|
||||||
|
v.showSvcPods(ns, s.Spec.Selector, v.backCmd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *svcView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *svcView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -111,15 +107,12 @@ func (v *svcView) benchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
svcType := strings.TrimSpace(tv.GetCell(r, tv.nameColIndex()+1).Text)
|
svcType := strings.TrimSpace(tv.GetCell(r, tv.nameColIndex()+1).Text)
|
||||||
log.Debug().Msgf("Service Type %q", svcType)
|
|
||||||
if svcType != "NodePort" && svcType != "LoadBalancer" {
|
if svcType != "NodePort" && svcType != "LoadBalancer" {
|
||||||
v.app.flash().err(errors.New("You must select a reachable service"))
|
v.app.flash().err(errors.New("You must select a reachable service"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ports := strings.TrimSpace(tv.GetCell(r, tv.nameColIndex()+5).Text)
|
ports := strings.TrimSpace(tv.GetCell(r, tv.nameColIndex()+5).Text)
|
||||||
// BOZO!! You Brute!!
|
|
||||||
// BOZO!! Will new much improv ie pop dialog and select port if multiport.
|
|
||||||
pp := strings.Split(ports, " ")
|
pp := strings.Split(ports, " ")
|
||||||
if len(pp) == 0 {
|
if len(pp) == 0 {
|
||||||
v.app.flash().err(errors.New("No ports found"))
|
v.app.flash().err(errors.New("No ports found"))
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -421,16 +422,23 @@ func (v *tableView) addHeaderCell(numCols map[string]bool, col int, name string,
|
||||||
c := tview.NewTableCell(v.sortIndicator(col, name))
|
c := tview.NewTableCell(v.sortIndicator(col, name))
|
||||||
{
|
{
|
||||||
c.SetExpansion(1)
|
c.SetExpansion(1)
|
||||||
c.SetTextColor(fg)
|
|
||||||
if numCols[name] || cpuRX.MatchString(name) || memRX.MatchString(name) {
|
if numCols[name] || cpuRX.MatchString(name) || memRX.MatchString(name) {
|
||||||
c.SetAlign(tview.AlignRight)
|
c.SetAlign(tview.AlignRight)
|
||||||
}
|
}
|
||||||
|
c.SetTextColor(fg)
|
||||||
c.SetBackgroundColor(bg)
|
c.SetBackgroundColor(bg)
|
||||||
}
|
}
|
||||||
v.SetCell(0, col, c)
|
v.SetCell(0, col, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) addBodyCell(numCols map[string]bool, header string, row, col int, field, delta string, color tcell.Color, pads maxyPad) {
|
func (v *tableView) addBodyCell(numCols map[string]bool, header string, row, col int, field, delta string, color tcell.Color, pads maxyPad) {
|
||||||
|
if header == "AGE" {
|
||||||
|
dur, err := time.ParseDuration(field)
|
||||||
|
if err == nil {
|
||||||
|
field = duration.HumanDuration(dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
field += deltas(delta, field)
|
field += deltas(delta, field)
|
||||||
align := tview.AlignLeft
|
align := tview.AlignLeft
|
||||||
if numCols[header] || cpuRX.MatchString(header) || memRX.MatchString(header) {
|
if numCols[header] || cpuRX.MatchString(header) || memRX.MatchString(header) {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,16 @@ func TestTVSortRows(t *testing.T) {
|
||||||
resource.Row{"x", "y"},
|
resource.Row{"x", "y"},
|
||||||
[]string{"row1", "row2"},
|
[]string{"row1", "row2"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
resource.RowEvents{
|
||||||
|
"row1": {Fields: resource.Row{"2175h48m0.06015s", "y"}},
|
||||||
|
"row2": {Fields: resource.Row{"403h42m34.060166s", "b"}},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
resource.Row{"403h42m34.060166s", "b"},
|
||||||
|
[]string{"row2", "row1"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var v *tableView
|
var v *tableView
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue