Skip to content

Commit d9d8ca6

Browse files
scout/usage: add --docker flag to scout usage tool (#985)
prints CPU and Memory usage for each running container in a docker deployment of sourcegraph
1 parent 4effeda commit d9d8ca6

File tree

1 file changed

+68
-9
lines changed

1 file changed

+68
-9
lines changed

internal/scout/usage/docker.go

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,86 @@ package usage
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67

8+
"github.com/charmbracelet/bubbles/table"
9+
"github.com/docker/docker/api/types"
710
"github.com/docker/docker/client"
11+
"github.com/sourcegraph/sourcegraph/lib/errors"
12+
"github.com/sourcegraph/src-cli/internal/scout/style"
813
)
914

1015
func Docker(ctx context.Context, client client.Client, opts ...Option) error {
1116
cfg := &Config{
12-
namespace: "default",
13-
docker: true,
14-
pod: "",
15-
container: "",
16-
spy: false,
17-
k8sClient: nil,
18-
dockerClient: &client,
19-
metricsClient: nil,
17+
namespace: "default",
18+
docker: true,
19+
pod: "",
20+
container: "",
21+
spy: false,
22+
dockerClient: &client,
2023
}
2124

2225
for _, opt := range opts {
2326
opt(cfg)
2427
}
2528

26-
fmt.Println("Command is routed, but not yet fixed.")
29+
containers, err := cfg.dockerClient.ContainerList(ctx, types.ContainerListOptions{})
30+
if err != nil {
31+
return errors.Wrap(err, "could not get list of containers")
32+
}
33+
34+
return renderDockerUsageTable(ctx, cfg, containers)
35+
}
36+
37+
// renderDockerUsageTable renders a table displaying CPU and memory usage for Docker containers.
38+
func renderDockerUsageTable(ctx context.Context, cfg *Config, containers []types.Container) error {
39+
columns := []table.Column{
40+
{Title: "Container", Width: 20},
41+
{Title: "Cores", Width: 10},
42+
{Title: "Usage", Width: 10},
43+
{Title: "Memory", Width: 10},
44+
{Title: "Usage", Width: 10},
45+
}
46+
rows := []table.Row{}
47+
48+
for _, container := range containers {
49+
containerInfo, err := cfg.dockerClient.ContainerInspect(ctx, container.ID)
50+
if err != nil {
51+
return errors.Wrap(err, "failed to get container info")
52+
}
53+
54+
stats, err := cfg.dockerClient.ContainerStats(ctx, container.ID, false)
55+
if err != nil {
56+
return errors.Wrap(err, "could not get container stats")
57+
}
58+
defer stats.Body.Close()
59+
60+
var usage types.StatsJSON
61+
if err := json.NewDecoder(stats.Body).Decode(&usage); err != nil {
62+
return errors.Wrap(err, "could not decode container stats")
63+
}
64+
65+
row := makeDockerUsageRow(usage, containerInfo)
66+
rows = append(rows, row)
67+
}
68+
69+
style.ResourceTable(columns, rows)
2770
return nil
2871
}
72+
73+
// makeDockerUsageRow generates a table row displaying CPU and memory usage for a Docker container.
74+
func makeDockerUsageRow(containerUsage types.StatsJSON, containerInfo types.ContainerJSON) table.Row {
75+
cpuCores := float64(containerInfo.HostConfig.NanoCPUs)
76+
memory := float64(containerInfo.HostConfig.Memory)
77+
cpuUsage := float64(containerUsage.CPUStats.CPUUsage.TotalUsage)
78+
memoryUsage := float64(containerUsage.MemoryStats.Usage)
79+
80+
return table.Row{
81+
containerInfo.Name,
82+
fmt.Sprintf("%.2f", cpuCores/1_000_000_000),
83+
fmt.Sprintf("%.2f%%", getPercentage(cpuUsage, cpuCores)),
84+
fmt.Sprintf("%.2fG", memory/1_000_000_000),
85+
fmt.Sprintf("%.2f%%", getPercentage(memoryUsage, memory)),
86+
}
87+
}

0 commit comments

Comments
 (0)