Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 0bee6d1

Browse files
authored
feat: add image:tag label to resources top (#300)
1 parent 8aa4a08 commit 0bee6d1

File tree

2 files changed

+58
-54
lines changed

2 files changed

+58
-54
lines changed

internal/cmd/resourcemanager.go

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"golang.org/x/xerrors"
1212

1313
"cdr.dev/coder-cli/coder-sdk"
14+
"cdr.dev/coder-cli/internal/coderutil"
15+
"cdr.dev/coder-cli/internal/x/xcobra"
1416
"cdr.dev/coder-cli/pkg/clog"
1517
)
1618

@@ -40,6 +42,7 @@ func resourceTop() *cobra.Command {
4042
Use: "top",
4143
Short: "resource viewer with Coder platform annotations",
4244
RunE: runResourceTop(&options),
45+
Args: xcobra.ExactArgs(0),
4346
Example: `coder resources top --group org
4447
coder resources top --group org --verbose --org DevOps
4548
coder resources top --group user --verbose --user [email protected]
@@ -82,6 +85,10 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
8285
if err != nil {
8386
return xerrors.Errorf("get users: %w", err)
8487
}
88+
images, err := coderutil.MakeImageMap(ctx, client, envs)
89+
if err != nil {
90+
return xerrors.Errorf("get images: %w", err)
91+
}
8592

8693
orgs, err := client.Organizations(ctx)
8794
if err != nil {
@@ -95,13 +102,20 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
95102

96103
var groups []groupable
97104
var labeler envLabeler
105+
data := entities{
106+
providers: providers.Kubernetes,
107+
users: users,
108+
orgs: orgs,
109+
envs: envs,
110+
images: images,
111+
}
98112
switch options.group {
99113
case "user":
100-
groups, labeler = aggregateByUser(providers.Kubernetes, users, orgs, envs, *options)
114+
groups, labeler = aggregateByUser(data, *options)
101115
case "org":
102-
groups, labeler = aggregateByOrg(providers.Kubernetes, users, orgs, envs, *options)
116+
groups, labeler = aggregateByOrg(data, *options)
103117
case "provider":
104-
groups, labeler = aggregateByProvider(providers.Kubernetes, users, orgs, envs, *options)
118+
groups, labeler = aggregateByProvider(data, *options)
105119
default:
106120
return xerrors.Errorf("unknown --group %q", options.group)
107121
}
@@ -110,27 +124,35 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
110124
}
111125
}
112126

113-
func aggregateByUser(providers []coder.KubernetesProvider, users []coder.User, orgs []coder.Organization, envs []coder.Environment, options resourceTopOptions) ([]groupable, envLabeler) {
127+
type entities struct {
128+
providers []coder.KubernetesProvider
129+
users []coder.User
130+
orgs []coder.Organization
131+
envs []coder.Environment
132+
images map[string]*coder.Image
133+
}
134+
135+
func aggregateByUser(data entities, options resourceTopOptions) ([]groupable, envLabeler) {
114136
var groups []groupable
115-
providerIDMap := providerIDs(providers)
137+
providerIDMap := providerIDs(data.providers)
116138
orgIDMap := make(map[string]coder.Organization)
117-
for _, o := range orgs {
139+
for _, o := range data.orgs {
118140
orgIDMap[o.ID] = o
119141
}
120-
userEnvs := make(map[string][]coder.Environment, len(users))
121-
for _, e := range envs {
142+
userEnvs := make(map[string][]coder.Environment, len(data.users))
143+
for _, e := range data.envs {
122144
if options.org != "" && orgIDMap[e.OrganizationID].Name != options.org {
123145
continue
124146
}
125147
userEnvs[e.UserID] = append(userEnvs[e.UserID], e)
126148
}
127-
for _, u := range users {
149+
for _, u := range data.users {
128150
if options.user != "" && u.Email != options.user {
129151
continue
130152
}
131153
groups = append(groups, userGrouping{user: u, envs: userEnvs[u.ID]})
132154
}
133-
return groups, labelAll(orgLabeler(orgIDMap), providerLabeler(providerIDMap))
155+
return groups, labelAll(imgLabeler(data.images), providerLabeler(providerIDMap), orgLabeler(orgIDMap))
134156
}
135157

136158
func userIDs(users []coder.User) map[string]coder.User {
@@ -141,24 +163,24 @@ func userIDs(users []coder.User) map[string]coder.User {
141163
return userIDMap
142164
}
143165

144-
func aggregateByOrg(providers []coder.KubernetesProvider, users []coder.User, orgs []coder.Organization, envs []coder.Environment, options resourceTopOptions) ([]groupable, envLabeler) {
166+
func aggregateByOrg(data entities, options resourceTopOptions) ([]groupable, envLabeler) {
145167
var groups []groupable
146-
providerIDMap := providerIDs(providers)
147-
orgEnvs := make(map[string][]coder.Environment, len(orgs))
148-
userIDMap := userIDs(users)
149-
for _, e := range envs {
168+
providerIDMap := providerIDs(data.providers)
169+
orgEnvs := make(map[string][]coder.Environment, len(data.orgs))
170+
userIDMap := userIDs(data.users)
171+
for _, e := range data.envs {
150172
if options.user != "" && userIDMap[e.UserID].Email != options.user {
151173
continue
152174
}
153175
orgEnvs[e.OrganizationID] = append(orgEnvs[e.OrganizationID], e)
154176
}
155-
for _, o := range orgs {
177+
for _, o := range data.orgs {
156178
if options.org != "" && o.Name != options.org {
157179
continue
158180
}
159181
groups = append(groups, orgGrouping{org: o, envs: orgEnvs[o.ID]})
160182
}
161-
return groups, labelAll(userLabeler(userIDMap), providerLabeler(providerIDMap))
183+
return groups, labelAll(imgLabeler(data.images), userLabeler(userIDMap), providerLabeler(providerIDMap))
162184
}
163185

164186
func providerIDs(providers []coder.KubernetesProvider) map[string]coder.KubernetesProvider {
@@ -169,24 +191,24 @@ func providerIDs(providers []coder.KubernetesProvider) map[string]coder.Kubernet
169191
return providerIDMap
170192
}
171193

172-
func aggregateByProvider(providers []coder.KubernetesProvider, users []coder.User, _ []coder.Organization, envs []coder.Environment, options resourceTopOptions) ([]groupable, envLabeler) {
194+
func aggregateByProvider(data entities, options resourceTopOptions) ([]groupable, envLabeler) {
173195
var groups []groupable
174-
providerIDMap := providerIDs(providers)
175-
userIDMap := userIDs(users)
176-
providerEnvs := make(map[string][]coder.Environment, len(providers))
177-
for _, e := range envs {
196+
providerIDMap := providerIDs(data.providers)
197+
userIDMap := userIDs(data.users)
198+
providerEnvs := make(map[string][]coder.Environment, len(data.providers))
199+
for _, e := range data.envs {
178200
if options.provider != "" && providerIDMap[e.ResourcePoolID].Name != options.provider {
179201
continue
180202
}
181203
providerEnvs[e.ResourcePoolID] = append(providerEnvs[e.ResourcePoolID], e)
182204
}
183-
for _, p := range providers {
205+
for _, p := range data.providers {
184206
if options.provider != "" && p.Name != options.provider {
185207
continue
186208
}
187209
groups = append(groups, providerGrouping{provider: p, envs: providerEnvs[p.ID]})
188210
}
189-
return groups, labelAll(userLabeler(userIDMap)) // TODO: consider adding an org label here
211+
return groups, labelAll(imgLabeler(data.images), userLabeler(userIDMap)) // TODO: consider adding an org label here
190212
}
191213

192214
// groupable specifies a structure capable of being an aggregation group of environments (user, org, all).
@@ -323,7 +345,7 @@ func resourcesFromEnv(env coder.Environment) resources {
323345
}
324346

325347
func fmtEnvResources(env coder.Environment, labeler envLabeler) string {
326-
return fmt.Sprintf("%s\t%s\t%s", env.Name, resourcesFromEnv(env), labeler.label(env))
348+
return fmt.Sprintf("%s\t%s\t%s", truncate(env.Name, 20, "..."), resourcesFromEnv(env), labeler.label(env))
327349
}
328350

329351
type envLabeler interface {
@@ -351,16 +373,10 @@ func (o orgLabeler) label(e coder.Environment) string {
351373
return fmt.Sprintf("[org: %s]", o[e.OrganizationID].Name)
352374
}
353375

354-
// TODO: implement
355-
//nolint
356-
type imgLabeler struct {
357-
imgMap map[string]coder.Image
358-
}
376+
type imgLabeler map[string]*coder.Image
359377

360-
// TODO: implement
361-
//nolint
362378
func (i imgLabeler) label(e coder.Environment) string {
363-
return fmt.Sprintf("[img: %s:%s]", i.imgMap[e.ImageID].Repository, e.ImageTag)
379+
return fmt.Sprintf("[img: %s:%s]", i[e.ImageID].Repository, e.ImageTag)
364380
}
365381

366382
type userLabeler map[string]coder.User
@@ -387,35 +403,22 @@ func aggregateEnvResources(envs []coder.Environment) resources {
387403
}
388404

389405
type resources struct {
390-
cpuAllocation float32
406+
cpuAllocation float32
407+
memAllocation float32
408+
409+
// TODO: consider using these
391410
cpuUtilization float32
392-
memAllocation float32
393411
memUtilization float32
394412
}
395413

396414
func (a resources) String() string {
397415
return fmt.Sprintf(
398-
"[cpu: alloc=%.1fvCPU]\t[mem: alloc=%.1fGB]",
416+
"[cpu: %.1f]\t[mem: %.1f GB]",
399417
a.cpuAllocation, a.memAllocation,
400418
)
401419
}
402420

403-
//nolint:unused
404-
func (a resources) cpuUtilPercentage() string {
405-
if a.cpuAllocation == 0 {
406-
return "N/A"
407-
}
408-
return fmt.Sprintf("%.1f%%", a.cpuUtilization/a.cpuAllocation*100)
409-
}
410-
411-
//nolint:unused
412-
func (a resources) memUtilPercentage() string {
413-
if a.memAllocation == 0 {
414-
return "N/A"
415-
}
416-
return fmt.Sprintf("%.1f%%", a.memUtilization/a.memAllocation*100)
417-
}
418-
421+
//nolint:unparam
419422
// truncate the given string and replace the removed chars with some replacement (ex: "...").
420423
func truncate(str string, max int, replace string) string {
421424
if len(str) <= max {

internal/coderutil/env.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ type EnvTable struct {
9191

9292
// EnvsHumanTable performs the composition of each Environment with its associated ProviderName and ImageRepo.
9393
func EnvsHumanTable(ctx context.Context, client coder.Client, envs []coder.Environment) ([]EnvTable, error) {
94-
imageMap, err := makeImageMap(ctx, client, envs)
94+
imageMap, err := MakeImageMap(ctx, client, envs)
9595
if err != nil {
9696
return nil, err
9797
}
@@ -124,7 +124,8 @@ func EnvsHumanTable(ctx context.Context, client coder.Client, envs []coder.Envir
124124
return pooledEnvs, nil
125125
}
126126

127-
func makeImageMap(ctx context.Context, client coder.Client, envs []coder.Environment) (map[string]*coder.Image, error) {
127+
// MakeImageMap fetches all image entities specified in the slice of environments, then places them into an ID map.
128+
func MakeImageMap(ctx context.Context, client coder.Client, envs []coder.Environment) (map[string]*coder.Image, error) {
128129
var (
129130
mu sync.Mutex
130131
egroup = clog.LoggedErrGroup()

0 commit comments

Comments
 (0)