Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort containers in index order #2878

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 53 additions & 26 deletions internal/dao/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ package dao
import (
"context"
"fmt"

"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
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"
Expand Down Expand Up @@ -46,14 +46,47 @@ func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error
if err != nil {
return nil, err
}
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
for _, co := range po.Spec.InitContainers {
res = append(res, makeContainerRes(co, po, cmx[co.Name], true))

type containerGroup struct {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to introduce these groups. If we modified makeContainerRes signature to take in a kind (I,M,E) and an index should be sufficient. Then getContainerStatus can just look up statuses based on kind and index.
What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@derailed It might be useful to add sidecar containers (S) to the type list. This would help solve this issue #2912

indexPrefix string
containers []v1.Container
statuses []v1.ContainerStatus
}
for _, co := range po.Spec.Containers {
res = append(res, makeContainerRes(co, po, cmx[co.Name], false))
containerGroups := []containerGroup{
{
indexPrefix: "E",
containers: convertEphemeralContainersToContainers(po.Spec.EphemeralContainers),
statuses: po.Status.EphemeralContainerStatuses,
},
{
indexPrefix: "I",
containers: po.Spec.InitContainers,
statuses: po.Status.InitContainerStatuses,
},
{
indexPrefix: "M",
containers: po.Spec.Containers,
statuses: po.Status.ContainerStatuses,
},
}

res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.EphemeralContainers)+len(po.Spec.Containers))
for _, group := range containerGroups {
for i, co := range group.containers {
var status *v1.ContainerStatus
if len(group.statuses) > i {
status = &group.statuses[i]
}

res = append(res, makeContainerRes(
fmt.Sprintf("%s%d", group.indexPrefix, i+1),
&co,
status,
cmx[co.Name],
po.GetCreationTimestamp(),
))
}
}
return res, nil
}

Expand All @@ -68,29 +101,14 @@ func (c *Container) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan,
// ----------------------------------------------------------------------------
// Helpers...

func makeContainerRes(co v1.Container, po *v1.Pod, cmx *mv1beta1.ContainerMetrics, isInit bool) render.ContainerRes {
func makeContainerRes(index string, co *v1.Container, status *v1.ContainerStatus, cmx *mv1beta1.ContainerMetrics, age metav1.Time) render.ContainerRes {
return render.ContainerRes{
Container: &co,
Status: getContainerStatus(co.Name, po.Status),
Index: index,
Container: co,
Status: status,
MX: cmx,
IsInit: isInit,
Age: po.GetCreationTimestamp(),
}
}

func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
for _, c := range status.ContainerStatuses {
if c.Name == co {
return &c
}
}
for _, c := range status.InitContainerStatuses {
if c.Name == co {
return &c
}
Age: age,
}

return nil
}

func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
Expand All @@ -102,3 +120,12 @@ func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
return &po, err
}

// convertEphemeralContainersToContainers reduces EphemeralContainers to common fields with Containers
func convertEphemeralContainersToContainers(ecos []v1.EphemeralContainer) []v1.Container {
cos := make([]v1.Container, len(ecos))
for i, eco := range ecos {
cos[i] = v1.Container(eco.EphemeralContainerCommon)
}
return cos
}
7 changes: 7 additions & 0 deletions internal/dao/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package dao_test

import (
"context"
"github.com/derailed/k9s/internal/render"
"testing"

"github.com/derailed/k9s/internal"
Expand Down Expand Up @@ -34,6 +35,12 @@ func TestContainerList(t *testing.T) {
oo, err := c.List(ctx, "")
assert.Nil(t, err)
assert.Equal(t, 1, len(oo))
assert.IsType(t, render.ContainerRes{}, oo[0])
cres := oo[0].(render.ContainerRes)
assert.Equal(t, "M1", cres.Index)
assert.Equal(t, "fred", cres.Container.Name)
assert.Equal(t, "fred", cres.Status.Name)
assert.NotEmpty(t, cres.Age)
}

// ----------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions internal/render/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ func (c Container) ColorerFunc() model1.ColorerFunc {
// Header returns a header row.
func (Container) Header(ns string) model1.Header {
return model1.Header{
model1.HeaderColumn{Name: "IDX", Align: tview.AlignRight},
model1.HeaderColumn{Name: "NAME"},
model1.HeaderColumn{Name: "PF"},
model1.HeaderColumn{Name: "IMAGE"},
model1.HeaderColumn{Name: "READY"},
model1.HeaderColumn{Name: "STATE"},
model1.HeaderColumn{Name: "INIT"},
model1.HeaderColumn{Name: "RESTARTS", Align: tview.AlignRight},
model1.HeaderColumn{Name: "PROBES(L:R)"},
model1.HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true},
Expand Down Expand Up @@ -109,12 +109,12 @@ func (c Container) Render(o interface{}, name string, r *model1.Row) error {

r.ID = co.Container.Name
r.Fields = model1.Fields{
co.Index,
co.Container.Name,
"●",
co.Container.Image,
ready,
state,
boolToStr(co.IsInit),
restarts,
probe(co.Container.LivenessProbe) + ":" + probe(co.Container.ReadinessProbe),
toMc(cur.cpu),
Expand Down Expand Up @@ -238,10 +238,10 @@ func probe(p *v1.Probe) string {

// ContainerRes represents a container and its metrics.
type ContainerRes struct {
ryanbrainard marked this conversation as resolved.
Show resolved Hide resolved
Index string
Container *v1.Container
Status *v1.ContainerStatus
MX *mv1beta1.ContainerMetrics
IsInit bool
Age metav1.Time
}

Expand Down
19 changes: 16 additions & 3 deletions internal/render/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package render_test

import (
"fmt"
"sort"
"testing"
"time"

Expand All @@ -17,26 +18,38 @@ import (
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)

func TestContainerRes_IndexSorting(t *testing.T) {
ephm1 := model1.Row{Fields: []string{"E1"}}
ephm10 := model1.Row{Fields: []string{"E10"}}
init1 := model1.Row{Fields: []string{"I1"}}
init2 := model1.Row{Fields: []string{"I2"}}
main1 := model1.Row{Fields: []string{"M1"}}

rows := model1.Rows{ephm1, ephm10, init1, init2, main1}
sorter := model1.RowSorter{Rows: rows, Index: 0, Asc: true}
assert.True(t, sort.IsSorted(sorter))
}

func TestContainer(t *testing.T) {
var c render.Container

cres := render.ContainerRes{
Index: "M1",
Container: makeContainer(),
Status: makeContainerStatus(),
MX: makeContainerMetrics(),
IsInit: false,
Age: makeAge(),
}
var r model1.Row
assert.Nil(t, c.Render(cres, "blee", &r))
assert.Equal(t, "fred", r.ID)
assert.Equal(t, model1.Fields{
"M1",
"fred",
"●",
"img",
"false",
"Running",
"false",
"0",
"off:off",
"10",
Expand All @@ -58,10 +71,10 @@ func BenchmarkContainerRender(b *testing.B) {
var c render.Container

cres := render.ContainerRes{
Index: "M1",
Container: makeContainer(),
Status: makeContainerStatus(),
MX: makeContainerMetrics(),
IsInit: false,
Age: makeAge(),
}
var r model1.Row
Expand Down
2 changes: 2 additions & 0 deletions internal/view/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewContainer(gvr client.GVR) ResourceViewer {
c.GetTable().SetDecorateFn(c.decorateRows)
c.AddBindKeysFn(c.bindKeys)
c.GetTable().SetDecorateFn(c.portForwardIndicator)
c.GetTable().SetSortCol("IDX", true)

return &c
}
Expand Down Expand Up @@ -90,6 +91,7 @@ func (c *Container) bindKeys(aa *ui.KeyActions) {
ui.KeyF: ui.NewKeyAction("Show PortForward", c.showPFCmd, true),
ui.KeyShiftF: ui.NewKeyAction("PortForward", c.portFwdCmd, true),
ui.KeyShiftT: ui.NewKeyAction("Sort Restart", c.GetTable().SortColCmd("RESTARTS", false), false),
ui.KeyShiftI: ui.NewKeyAction("Sort Index", c.GetTable().SortColCmd("IDX", true), false),
})
aa.Merge(resourceSorters(c.GetTable()))
}
Expand Down
2 changes: 1 addition & 1 deletion internal/view/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func TestContainerNew(t *testing.T) {

assert.Nil(t, c.Init(makeCtx()))
assert.Equal(t, "Containers", c.Name())
assert.Equal(t, 18, len(c.Hints()))
assert.Equal(t, 19, len(c.Hints()))
}