@@ -11,6 +11,8 @@ import (
11
11
"golang.org/x/xerrors"
12
12
13
13
"cdr.dev/coder-cli/coder-sdk"
14
+ "cdr.dev/coder-cli/internal/coderutil"
15
+ "cdr.dev/coder-cli/internal/x/xcobra"
14
16
"cdr.dev/coder-cli/pkg/clog"
15
17
)
16
18
@@ -40,6 +42,7 @@ func resourceTop() *cobra.Command {
40
42
Use : "top" ,
41
43
Short : "resource viewer with Coder platform annotations" ,
42
44
RunE : runResourceTop (& options ),
45
+ Args : xcobra .ExactArgs (0 ),
43
46
Example : `coder resources top --group org
44
47
coder resources top --group org --verbose --org DevOps
45
48
coder resources top --group user --verbose --user [email protected]
@@ -82,6 +85,10 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
82
85
if err != nil {
83
86
return xerrors .Errorf ("get users: %w" , err )
84
87
}
88
+ images , err := coderutil .MakeImageMap (ctx , client , envs )
89
+ if err != nil {
90
+ return xerrors .Errorf ("get images: %w" , err )
91
+ }
85
92
86
93
orgs , err := client .Organizations (ctx )
87
94
if err != nil {
@@ -95,13 +102,20 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
95
102
96
103
var groups []groupable
97
104
var labeler envLabeler
105
+ data := entities {
106
+ providers : providers .Kubernetes ,
107
+ users : users ,
108
+ orgs : orgs ,
109
+ envs : envs ,
110
+ images : images ,
111
+ }
98
112
switch options .group {
99
113
case "user" :
100
- groups , labeler = aggregateByUser (providers . Kubernetes , users , orgs , envs , * options )
114
+ groups , labeler = aggregateByUser (data , * options )
101
115
case "org" :
102
- groups , labeler = aggregateByOrg (providers . Kubernetes , users , orgs , envs , * options )
116
+ groups , labeler = aggregateByOrg (data , * options )
103
117
case "provider" :
104
- groups , labeler = aggregateByProvider (providers . Kubernetes , users , orgs , envs , * options )
118
+ groups , labeler = aggregateByProvider (data , * options )
105
119
default :
106
120
return xerrors .Errorf ("unknown --group %q" , options .group )
107
121
}
@@ -110,27 +124,35 @@ func runResourceTop(options *resourceTopOptions) func(cmd *cobra.Command, args [
110
124
}
111
125
}
112
126
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 ) {
114
136
var groups []groupable
115
- providerIDMap := providerIDs (providers )
137
+ providerIDMap := providerIDs (data . providers )
116
138
orgIDMap := make (map [string ]coder.Organization )
117
- for _ , o := range orgs {
139
+ for _ , o := range data . orgs {
118
140
orgIDMap [o .ID ] = o
119
141
}
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 {
122
144
if options .org != "" && orgIDMap [e .OrganizationID ].Name != options .org {
123
145
continue
124
146
}
125
147
userEnvs [e .UserID ] = append (userEnvs [e .UserID ], e )
126
148
}
127
- for _ , u := range users {
149
+ for _ , u := range data . users {
128
150
if options .user != "" && u .Email != options .user {
129
151
continue
130
152
}
131
153
groups = append (groups , userGrouping {user : u , envs : userEnvs [u .ID ]})
132
154
}
133
- return groups , labelAll (orgLabeler ( orgIDMap ), providerLabeler (providerIDMap ))
155
+ return groups , labelAll (imgLabeler ( data . images ), providerLabeler (providerIDMap ), orgLabeler ( orgIDMap ))
134
156
}
135
157
136
158
func userIDs (users []coder.User ) map [string ]coder.User {
@@ -141,24 +163,24 @@ func userIDs(users []coder.User) map[string]coder.User {
141
163
return userIDMap
142
164
}
143
165
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 ) {
145
167
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 {
150
172
if options .user != "" && userIDMap [e .UserID ].Email != options .user {
151
173
continue
152
174
}
153
175
orgEnvs [e .OrganizationID ] = append (orgEnvs [e .OrganizationID ], e )
154
176
}
155
- for _ , o := range orgs {
177
+ for _ , o := range data . orgs {
156
178
if options .org != "" && o .Name != options .org {
157
179
continue
158
180
}
159
181
groups = append (groups , orgGrouping {org : o , envs : orgEnvs [o .ID ]})
160
182
}
161
- return groups , labelAll (userLabeler (userIDMap ), providerLabeler (providerIDMap ))
183
+ return groups , labelAll (imgLabeler ( data . images ), userLabeler (userIDMap ), providerLabeler (providerIDMap ))
162
184
}
163
185
164
186
func providerIDs (providers []coder.KubernetesProvider ) map [string ]coder.KubernetesProvider {
@@ -169,24 +191,24 @@ func providerIDs(providers []coder.KubernetesProvider) map[string]coder.Kubernet
169
191
return providerIDMap
170
192
}
171
193
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 ) {
173
195
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 {
178
200
if options .provider != "" && providerIDMap [e .ResourcePoolID ].Name != options .provider {
179
201
continue
180
202
}
181
203
providerEnvs [e .ResourcePoolID ] = append (providerEnvs [e .ResourcePoolID ], e )
182
204
}
183
- for _ , p := range providers {
205
+ for _ , p := range data . providers {
184
206
if options .provider != "" && p .Name != options .provider {
185
207
continue
186
208
}
187
209
groups = append (groups , providerGrouping {provider : p , envs : providerEnvs [p .ID ]})
188
210
}
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
190
212
}
191
213
192
214
// 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 {
323
345
}
324
346
325
347
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 ))
327
349
}
328
350
329
351
type envLabeler interface {
@@ -351,16 +373,10 @@ func (o orgLabeler) label(e coder.Environment) string {
351
373
return fmt .Sprintf ("[org: %s]" , o [e .OrganizationID ].Name )
352
374
}
353
375
354
- // TODO: implement
355
- //nolint
356
- type imgLabeler struct {
357
- imgMap map [string ]coder.Image
358
- }
376
+ type imgLabeler map [string ]* coder.Image
359
377
360
- // TODO: implement
361
- //nolint
362
378
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 )
364
380
}
365
381
366
382
type userLabeler map [string ]coder.User
@@ -387,35 +403,22 @@ func aggregateEnvResources(envs []coder.Environment) resources {
387
403
}
388
404
389
405
type resources struct {
390
- cpuAllocation float32
406
+ cpuAllocation float32
407
+ memAllocation float32
408
+
409
+ // TODO: consider using these
391
410
cpuUtilization float32
392
- memAllocation float32
393
411
memUtilization float32
394
412
}
395
413
396
414
func (a resources ) String () string {
397
415
return fmt .Sprintf (
398
- "[cpu: alloc=%.1fvCPU ]\t [mem: alloc=%.1fGB ]" ,
416
+ "[cpu: %.1f ]\t [mem: %.1f GB ]" ,
399
417
a .cpuAllocation , a .memAllocation ,
400
418
)
401
419
}
402
420
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
419
422
// truncate the given string and replace the removed chars with some replacement (ex: "...").
420
423
func truncate (str string , max int , replace string ) string {
421
424
if len (str ) <= max {
0 commit comments