From f9e32003b94c1d9c31e79adb96d85629971f32b1 Mon Sep 17 00:00:00 2001 From: derailed Date: Wed, 22 Jan 2020 13:42:51 -0700 Subject: [PATCH] reworked cpu/mem limits --- internal/model/table_int_test.go | 4 +-- internal/model/table_test.go | 2 +- internal/render/container.go | 18 ++++++++-- internal/render/container_test.go | 2 ++ internal/render/pod.go | 57 +++++++++++++++++++------------ internal/render/pod_test.go | 12 +++---- internal/view/help_test.go | 2 +- internal/view/pod.go | 6 ++-- internal/view/pod_test.go | 2 +- 9 files changed, 68 insertions(+), 37 deletions(-) diff --git a/internal/model/table_int_test.go b/internal/model/table_int_test.go index 19fd80d9..3ba5405d 100644 --- a/internal/model/table_int_test.go +++ b/internal/model/table_int_test.go @@ -32,7 +32,7 @@ func TestTableReconcile(t *testing.T) { err := ta.reconcile(ctx) assert.Nil(t, err) data := ta.Peek() - assert.Equal(t, 13, len(data.Header)) + assert.Equal(t, 15, len(data.Header)) assert.Equal(t, 1, len(data.RowEvents)) assert.Equal(t, client.NamespaceAll, data.Namespace) } @@ -110,7 +110,7 @@ func TestTableHydrate(t *testing.T) { assert.Nil(t, hydrate("blee", oo, rr, render.Pod{})) assert.Equal(t, 1, len(rr)) - assert.Equal(t, 12, len(rr[0].Fields)) + assert.Equal(t, 14, len(rr[0].Fields)) } func TestTableGenericHydrate(t *testing.T) { diff --git a/internal/model/table_test.go b/internal/model/table_test.go index f00cde91..b72878ca 100644 --- a/internal/model/table_test.go +++ b/internal/model/table_test.go @@ -32,7 +32,7 @@ func TestTableRefresh(t *testing.T) { ctx = context.WithValue(ctx, internal.KeyFields, "") ta.Refresh(ctx) data := ta.Peek() - assert.Equal(t, 13, len(data.Header)) + assert.Equal(t, 15, len(data.Header)) assert.Equal(t, 1, len(data.RowEvents)) assert.Equal(t, client.NamespaceAll, data.Namespace) assert.Equal(t, 1, l.count) diff --git a/internal/render/container.go b/internal/render/container.go index e1f42132..77ea3b71 100644 --- a/internal/render/container.go +++ b/internal/render/container.go @@ -77,6 +77,8 @@ func (Container) Header(ns string) HeaderRow { Header{Name: "MEM", Align: tview.AlignRight}, Header{Name: "%CPU", Align: tview.AlignRight}, Header{Name: "%MEM", Align: tview.AlignRight}, + Header{Name: "%MAX-CPU", Align: tview.AlignRight}, + Header{Name: "%MAX-MEM", Align: tview.AlignRight}, Header{Name: "PORTS"}, Header{Name: "AGE", Decorator: AgeDecorator}, } @@ -89,7 +91,7 @@ func (c Container) Render(o interface{}, name string, r *Row) error { return fmt.Errorf("Expected ContainerRes, but got %T", o) } - cur, perc := gatherMetrics(co.Container, co.MX) + cur, perc, limit := gatherMetrics(co.Container, co.MX) ready, state, restarts := "false", MissingValue, "0" if co.Status != nil { ready, state, restarts = boolToStr(co.Status.Ready), toState(co.Status.State), strconv.Itoa(int(co.Status.RestartCount)) @@ -109,6 +111,8 @@ func (c Container) Render(o interface{}, name string, r *Row) error { cur.mem, perc.cpu, perc.mem, + limit.cpu, + limit.mem, toStrPorts(co.Container.Ports), toAge(co.Age), ) @@ -119,8 +123,8 @@ func (c Container) Render(o interface{}, name string, r *Row) error { // ---------------------------------------------------------------------------- // Helpers... -func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (c, p metric) { - c, p = noMetric(), noMetric() +func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (c, p, l metric) { + c, p, l = noMetric(), noMetric(), noMetric() if mx == nil { return } @@ -140,6 +144,14 @@ func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (c, p metric p.mem = AsPerc(toPerc(mem, ToMB(rmem.Value()))) } + lcpu, lmem := containerLimits(*co) + if lcpu != nil { + l.cpu = AsPerc(toPerc(float64(cpu), float64(lcpu.MilliValue()))) + } + if lmem != nil { + l.mem = AsPerc(toPerc(mem, ToMB(lmem.Value()))) + } + return } diff --git a/internal/render/container_test.go b/internal/render/container_test.go index 3dac3058..051cb2fb 100644 --- a/internal/render/container_test.go +++ b/internal/render/container_test.go @@ -38,6 +38,8 @@ func TestContainer(t *testing.T) { "20", "50", "20", + "50", + "20", "", }, r.Fields[:len(r.Fields)-1], diff --git a/internal/render/pod.go b/internal/render/pod.go index fe2303ea..24495cec 100644 --- a/internal/render/pod.go +++ b/internal/render/pod.go @@ -75,11 +75,13 @@ func (Pod) Header(ns string) HeaderRow { Header{Name: "NAME"}, Header{Name: "READY"}, Header{Name: "STATUS"}, - Header{Name: "RESTART", Align: tview.AlignRight}, + Header{Name: "RS", Align: tview.AlignRight}, Header{Name: "CPU", Align: tview.AlignRight}, Header{Name: "MEM", Align: tview.AlignRight}, - Header{Name: "%CPU (LIM)", Align: tview.AlignRight}, - Header{Name: "%MEM (LIM)", Align: tview.AlignRight}, + Header{Name: "%CPU", Align: tview.AlignRight}, + Header{Name: "%MEM", Align: tview.AlignRight}, + Header{Name: "%MAX_CPU", Align: tview.AlignRight}, + Header{Name: "%MAX_MEM", Align: tview.AlignRight}, Header{Name: "IP"}, Header{Name: "NODE"}, Header{Name: "QOS"}, @@ -116,8 +118,10 @@ func (p Pod) Render(o interface{}, ns string, r *Row) error { strconv.Itoa(rc), c.cpu, c.mem, - perc.cpu+" ("+fmt.Sprintf("%3v",perc.cpuLim)+")", - perc.mem+" ("+fmt.Sprintf("%3v",perc.memLim)+")", + perc.cpu, + perc.mem, + perc.cpuLim, + perc.memLim, na(po.Status.PodIP), na(po.Spec.NodeName), p.mapQOS(po.Status.QOSClass), @@ -158,11 +162,11 @@ func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (c, p metric) { mem: ToMi(ToMB(mem.Value())), } - rc, rm := requestedRes(pod) - lc, lm := resourceLimits(pod) + rc, rm := requestedRes(pod.Spec.Containers) + lc, lm := resourceLimits(pod.Spec.Containers) p = metric{ - cpu: AsPerc(toPerc(float64(cpu.MilliValue()), float64(rc.MilliValue()))), - mem: AsPerc(toPerc(ToMB(mem.Value()), ToMB(rm.Value()))), + cpu: AsPerc(toPerc(float64(cpu.MilliValue()), float64(rc.MilliValue()))), + mem: AsPerc(toPerc(ToMB(mem.Value()), ToMB(rm.Value()))), cpuLim: AsPerc(toPerc(float64(cpu.MilliValue()), float64(lc.MilliValue()))), memLim: AsPerc(toPerc(ToMB(mem.Value()), ToMB(lm.Value()))), } @@ -172,7 +176,6 @@ func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (c, p metric) { func containerResources(co v1.Container) (cpu, mem *resource.Quantity) { req, limit := co.Resources.Requests, co.Resources.Limits - switch { case len(req) != 0: cpu, mem = req.Cpu(), req.Memory() @@ -183,23 +186,32 @@ func containerResources(co v1.Container) (cpu, mem *resource.Quantity) { return } -func resourceLimits(po *v1.Pod) (cpu, mem resource.Quantity) { - for _, co := range po.Spec.Containers { +func containerLimits(co v1.Container) (cpu, mem *resource.Quantity) { + limit := co.Resources.Limits + if len(limit) == 0 { + return nil, nil + } + return limit.Cpu(), limit.Memory() +} + +func resourceLimits(cc []v1.Container) (cpu, mem resource.Quantity) { + for _, co := range cc { limit := co.Resources.Limits - if len(limit) != 0 { - if limit.Cpu() != nil { - cpu.Add(*limit.Cpu()) - } - if limit.Memory() != nil { - mem.Add(*limit.Memory()) - } + if len(limit) == 0 { + continue + } + if limit.Cpu() != nil { + cpu.Add(*limit.Cpu()) + } + if limit.Memory() != nil { + mem.Add(*limit.Memory()) } } return } -func requestedRes(po *v1.Pod) (cpu, mem resource.Quantity) { - for _, co := range po.Spec.Containers { +func requestedRes(cc []v1.Container) (cpu, mem resource.Quantity) { + for _, co := range cc { c, m := containerResources(co) if c != nil { cpu.Add(*c) @@ -212,6 +224,9 @@ func requestedRes(po *v1.Pod) (cpu, mem resource.Quantity) { } func currentRes(mx *mv1beta1.PodMetrics) (cpu, mem resource.Quantity) { + if mx == nil { + return + } for _, co := range mx.Containers { c, m := co.Usage.Cpu(), co.Usage.Memory() cpu.Add(*c) diff --git a/internal/render/pod_test.go b/internal/render/pod_test.go index e03ffcfe..6b0df397 100644 --- a/internal/render/pod_test.go +++ b/internal/render/pod_test.go @@ -65,13 +65,13 @@ func TestPodRender(t *testing.T) { } var po render.Pod - r := render.NewRow(12) + r := render.NewRow(14) err := po.Render(&pom, "", &r) assert.Nil(t, err) assert.Equal(t, "default/nginx", r.ID) - e := render.Fields{"default", "nginx", "1/1", "Running", "0", "10", "10", "10 ( 0)", "14 ( 5)", "172.17.0.6", "minikube", "BE"} - assert.Equal(t, e, r.Fields[:12]) + e := render.Fields{"default", "nginx", "1/1", "Running", "0", "10", "10", "10", "14", "0", "5", "172.17.0.6", "minikube", "BE"} + assert.Equal(t, e, r.Fields[:14]) } func BenchmarkPodRender(b *testing.B) { @@ -96,13 +96,13 @@ func TestPodInitRender(t *testing.T) { } var po render.Pod - r := render.NewRow(12) + r := render.NewRow(14) err := po.Render(&pom, "", &r) assert.Nil(t, err) assert.Equal(t, "default/nginx", r.ID) - e := render.Fields{"default", "nginx", "1/1", "Init:0/1", "0", "10", "10", "10 ( 0)", "14 ( 5)", "172.17.0.6", "minikube", "BE"} - assert.Equal(t, e, r.Fields[:12]) + e := render.Fields{"default", "nginx", "1/1", "Init:0/1", "0", "10", "10", "10", "14", "0", "5", "172.17.0.6", "minikube", "BE"} + assert.Equal(t, e, r.Fields[:14]) } // ---------------------------------------------------------------------------- diff --git a/internal/view/help_test.go b/internal/view/help_test.go index 03e13962..18c5ccd3 100644 --- a/internal/view/help_test.go +++ b/internal/view/help_test.go @@ -21,7 +21,7 @@ func TestHelp(t *testing.T) { v := view.NewHelp() assert.Nil(t, v.Init(ctx)) - assert.Equal(t, 17, v.GetRowCount()) + assert.Equal(t, 19, v.GetRowCount()) assert.Equal(t, 8, v.GetColumnCount()) assert.Equal(t, "", strings.TrimSpace(v.GetCell(1, 0).Text)) assert.Equal(t, "Kill", strings.TrimSpace(v.GetCell(1, 1).Text)) diff --git a/internal/view/pod.go b/internal/view/pod.go index 14e3e0a9..d11b2120 100644 --- a/internal/view/pod.go +++ b/internal/view/pod.go @@ -48,8 +48,10 @@ func (p *Pod) bindKeys(aa ui.KeyActions) { ui.KeyShiftM: ui.NewKeyAction("Sort MEM", p.GetTable().SortColCmd(5, false), false), ui.KeyShiftX: ui.NewKeyAction("Sort CPU%", p.GetTable().SortColCmd(6, false), false), ui.KeyShiftZ: ui.NewKeyAction("Sort MEM%", p.GetTable().SortColCmd(7, false), false), - ui.KeyShiftI: ui.NewKeyAction("Sort IP", p.GetTable().SortColCmd(8, true), false), - ui.KeyShiftO: ui.NewKeyAction("Sort Node", p.GetTable().SortColCmd(9, true), false), + tcell.KeyCtrlX: ui.NewKeyAction("Sort CPU% LIMITS", p.GetTable().SortColCmd(8, false), false), + tcell.KeyCtrlZ: ui.NewKeyAction("Sort MEM% LIMITS", p.GetTable().SortColCmd(9, false), false), + ui.KeyShiftI: ui.NewKeyAction("Sort IP", p.GetTable().SortColCmd(10, true), false), + ui.KeyShiftO: ui.NewKeyAction("Sort Node", p.GetTable().SortColCmd(11, true), false), }) } diff --git a/internal/view/pod_test.go b/internal/view/pod_test.go index 16095f15..00878bdb 100644 --- a/internal/view/pod_test.go +++ b/internal/view/pod_test.go @@ -16,7 +16,7 @@ func TestPodNew(t *testing.T) { assert.Nil(t, po.Init(makeCtx())) assert.Equal(t, "Pods", po.Name()) - assert.Equal(t, 16, len(po.Hints())) + assert.Equal(t, 18, len(po.Hints())) } // Helpers...