top: expose container labels

Signed-off-by: Dominik Menke <dom@digineo.de>
This commit is contained in:
Dominik Menke 2024-12-17 11:00:07 +01:00 committed by Nicolas De loof
parent a766e1669a
commit 375a279785
4 changed files with 90 additions and 60 deletions

View File

@ -73,14 +73,23 @@ func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opt
func collectTop(containers []api.ContainerProcSummary) (topHeader, []topEntries) { func collectTop(containers []api.ContainerProcSummary) (topHeader, []topEntries) {
// map column name to its header (should keep working if backend.Top returns // map column name to its header (should keep working if backend.Top returns
// varying columns for different containers) // varying columns for different containers)
header := topHeader{"SERVICE": 0} header := topHeader{"SERVICE": 0, "#": 1}
// assume one process per container and grow if needed // assume one process per container and grow if needed
entries := make([]topEntries, 0, len(containers)) entries := make([]topEntries, 0, len(containers))
for _, container := range containers { for _, container := range containers {
for _, proc := range container.Processes { for _, proc := range container.Processes {
entry := topEntries{"SERVICE": container.Name} svc := container.Name
if tmp, ok := container.Labels[api.ServiceLabel]; ok {
svc = tmp
}
replica := "-"
if tmp, ok := container.Labels[api.ContainerNumberLabel]; ok {
replica = tmp
}
entry := topEntries{"SERVICE": svc, "#": replica}
for i, title := range container.Titles { for i, title := range container.Titles {
if _, exists := header[title]; !exists { if _, exists := header[title]; !exists {

View File

@ -39,7 +39,7 @@ var topTestCases = []struct {
name: "noprocs", name: "noprocs",
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}, titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{}, procs: [][]string{},
header: topHeader{"SERVICE": 0}, header: topHeader{"SERVICE": 0, "#": 1},
entries: []topEntries{}, entries: []topEntries{},
output: "", output: "",
}, },
@ -49,18 +49,20 @@ var topTestCases = []struct {
procs: [][]string{{"root", "1", "1", "0", "12:00", "?", "00:00:01", "/entrypoint"}}, procs: [][]string{{"root", "1", "1", "0", "12:00", "?", "00:00:01", "/entrypoint"}},
header: topHeader{ header: topHeader{
"SERVICE": 0, "SERVICE": 0,
"UID": 1, "#": 1,
"PID": 2, "UID": 2,
"PPID": 3, "PID": 3,
"C": 4, "PPID": 4,
"STIME": 5, "C": 5,
"TTY": 6, "STIME": 6,
"TIME": 7, "TTY": 7,
"CMD": 8, "TIME": 8,
"CMD": 9,
}, },
entries: []topEntries{ entries: []topEntries{
{ {
"SERVICE": "simple", "SERVICE": "simple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"PPID": "1", "PPID": "1",
@ -72,8 +74,8 @@ var topTestCases = []struct {
}, },
}, },
output: trim(` output: trim(`
SERVICE UID PID PPID C STIME TTY TIME CMD SERVICE # UID PID PPID C STIME TTY TIME CMD
simple root 1 1 0 12:00 ? 00:00:01 /entrypoint simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint
`), `),
}, },
{ {
@ -82,17 +84,19 @@ var topTestCases = []struct {
procs: [][]string{{"root", "1", "0", "12:00", "?", "00:00:02", "/entrypoint"}}, procs: [][]string{{"root", "1", "0", "12:00", "?", "00:00:02", "/entrypoint"}},
header: topHeader{ header: topHeader{
"SERVICE": 0, "SERVICE": 0,
"UID": 1, "#": 1,
"PID": 2, "UID": 2,
"C": 3, "PID": 3,
"STIME": 4, "C": 4,
"TTY": 5, "STIME": 5,
"TIME": 6, "TTY": 6,
"CMD": 7, "TIME": 7,
"CMD": 8,
}, },
entries: []topEntries{ entries: []topEntries{
{ {
"SERVICE": "noppid", "SERVICE": "noppid",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"C": "0", "C": "0",
@ -103,8 +107,8 @@ var topTestCases = []struct {
}, },
}, },
output: trim(` output: trim(`
SERVICE UID PID C STIME TTY TIME CMD SERVICE # UID PID C STIME TTY TIME CMD
noppid root 1 0 12:00 ? 00:00:02 /entrypoint noppid 1 root 1 0 12:00 ? 00:00:02 /entrypoint
`), `),
}, },
{ {
@ -113,19 +117,21 @@ var topTestCases = []struct {
procs: [][]string{{"root", "1", "1", "1", "0", "12:00", "?", "00:00:03", "/entrypoint"}}, procs: [][]string{{"root", "1", "1", "1", "0", "12:00", "?", "00:00:03", "/entrypoint"}},
header: topHeader{ header: topHeader{
"SERVICE": 0, "SERVICE": 0,
"UID": 1, "#": 1,
"GID": 2, "UID": 2,
"PID": 3, "GID": 3,
"PPID": 4, "PID": 4,
"C": 5, "PPID": 5,
"STIME": 6, "C": 6,
"TTY": 7, "STIME": 7,
"TIME": 8, "TTY": 8,
"CMD": 9, "TIME": 9,
"CMD": 10,
}, },
entries: []topEntries{ entries: []topEntries{
{ {
"SERVICE": "extra-hdr", "SERVICE": "extra-hdr",
"#": "1",
"UID": "root", "UID": "root",
"GID": "1", "GID": "1",
"PID": "1", "PID": "1",
@ -138,8 +144,8 @@ var topTestCases = []struct {
}, },
}, },
output: trim(` output: trim(`
SERVICE UID GID PID PPID C STIME TTY TIME CMD SERVICE # UID GID PID PPID C STIME TTY TIME CMD
extra-hdr root 1 1 1 0 12:00 ? 00:00:03 /entrypoint extra-hdr 1 root 1 1 1 0 12:00 ? 00:00:03 /entrypoint
`), `),
}, },
{ {
@ -151,18 +157,20 @@ var topTestCases = []struct {
}, },
header: topHeader{ header: topHeader{
"SERVICE": 0, "SERVICE": 0,
"UID": 1, "#": 1,
"PID": 2, "UID": 2,
"PPID": 3, "PID": 3,
"C": 4, "PPID": 4,
"STIME": 5, "C": 5,
"TTY": 6, "STIME": 6,
"TIME": 7, "TTY": 7,
"CMD": 8, "TIME": 8,
"CMD": 9,
}, },
entries: []topEntries{ entries: []topEntries{
{ {
"SERVICE": "multiple", "SERVICE": "multiple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"PPID": "1", "PPID": "1",
@ -174,6 +182,7 @@ var topTestCases = []struct {
}, },
{ {
"SERVICE": "multiple", "SERVICE": "multiple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "123", "PID": "123",
"PPID": "1", "PPID": "1",
@ -185,9 +194,9 @@ var topTestCases = []struct {
}, },
}, },
output: trim(` output: trim(`
SERVICE UID PID PPID C STIME TTY TIME CMD SERVICE # UID PID PPID C STIME TTY TIME CMD
multiple root 1 1 0 12:00 ? 00:00:04 /entrypoint multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint
multiple root 123 1 0 12:00 ? 00:00:42 sleep infinity multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity
`), `),
}, },
} }
@ -201,9 +210,13 @@ func TestRunTopCore(t *testing.T) {
for _, tc := range topTestCases { for _, tc := range topTestCases {
summary := api.ContainerProcSummary{ summary := api.ContainerProcSummary{
Name: tc.name, Name: "not used",
Titles: tc.titles, Titles: tc.titles,
Processes: tc.procs, Processes: tc.procs,
Labels: map[string]string{
api.ServiceLabel: tc.name,
api.ContainerNumberLabel: "1",
},
} }
all = append(all, summary) all = append(all, summary)
@ -224,19 +237,21 @@ func TestRunTopCore(t *testing.T) {
header, entries := collectTop(all) header, entries := collectTop(all)
assert.EqualValues(t, topHeader{ assert.EqualValues(t, topHeader{
"SERVICE": 0, "SERVICE": 0,
"UID": 1, "#": 1,
"PID": 2, "UID": 2,
"PPID": 3, "PID": 3,
"C": 4, "PPID": 4,
"STIME": 5, "C": 5,
"TTY": 6, "STIME": 6,
"TIME": 7, "TTY": 7,
"CMD": 8, "TIME": 8,
"GID": 9, "CMD": 9,
"GID": 10,
}, header) }, header)
assert.EqualValues(t, []topEntries{ assert.EqualValues(t, []topEntries{
{ {
"SERVICE": "simple", "SERVICE": "simple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"PPID": "1", "PPID": "1",
@ -247,6 +262,7 @@ func TestRunTopCore(t *testing.T) {
"CMD": "/entrypoint", "CMD": "/entrypoint",
}, { }, {
"SERVICE": "noppid", "SERVICE": "noppid",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"C": "0", "C": "0",
@ -256,6 +272,7 @@ func TestRunTopCore(t *testing.T) {
"CMD": "/entrypoint", "CMD": "/entrypoint",
}, { }, {
"SERVICE": "extra-hdr", "SERVICE": "extra-hdr",
"#": "1",
"UID": "root", "UID": "root",
"GID": "1", "GID": "1",
"PID": "1", "PID": "1",
@ -267,6 +284,7 @@ func TestRunTopCore(t *testing.T) {
"CMD": "/entrypoint", "CMD": "/entrypoint",
}, { }, {
"SERVICE": "multiple", "SERVICE": "multiple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "1", "PID": "1",
"PPID": "1", "PPID": "1",
@ -277,6 +295,7 @@ func TestRunTopCore(t *testing.T) {
"CMD": "/entrypoint", "CMD": "/entrypoint",
}, { }, {
"SERVICE": "multiple", "SERVICE": "multiple",
"#": "1",
"UID": "root", "UID": "root",
"PID": "123", "PID": "123",
"PPID": "1", "PPID": "1",
@ -292,12 +311,12 @@ func TestRunTopCore(t *testing.T) {
err := topPrint(&buf, header, entries) err := topPrint(&buf, header, entries)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, trim(` assert.Equal(t, trim(`
SERVICE UID PID PPID C STIME TTY TIME CMD GID SERVICE # UID PID PPID C STIME TTY TIME CMD GID
simple root 1 1 0 12:00 ? 00:00:01 /entrypoint - simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint -
noppid root 1 - 0 12:00 ? 00:00:02 /entrypoint - noppid 1 root 1 - 0 12:00 ? 00:00:02 /entrypoint -
extra-hdr root 1 1 0 12:00 ? 00:00:03 /entrypoint 1 extra-hdr 1 root 1 1 0 12:00 ? 00:00:03 /entrypoint 1
multiple root 1 1 0 12:00 ? 00:00:04 /entrypoint - multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint -
multiple root 123 1 0 12:00 ? 00:00:42 sleep infinity - multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity -
`), buf.String()) `), buf.String())
}) })

View File

@ -523,6 +523,7 @@ type ContainerProcSummary struct {
Name string Name string
Processes [][]string Processes [][]string
Titles []string Titles []string
Labels map[string]string
} }
// ImageSummary holds container image description // ImageSummary holds container image description

View File

@ -47,6 +47,7 @@ func (s *composeService) Top(ctx context.Context, projectName string, services [
Name: getCanonicalContainerName(ctr), Name: getCanonicalContainerName(ctr),
Processes: topContent.Processes, Processes: topContent.Processes,
Titles: topContent.Titles, Titles: topContent.Titles,
Labels: container.Labels,
} }
return nil return nil
}) })