@@ -3,6 +3,7 @@ package cmd
3
3
import (
4
4
"fmt"
5
5
"os"
6
+ "sort"
6
7
"text/tabwriter"
7
8
8
9
"cdr.dev/coder-cli/coder-sdk"
@@ -14,60 +15,81 @@ func makeResourceCmd() *cobra.Command {
14
15
Use :"resources" ,
15
16
Short :"manager Coder resources with platform-level context (users, organizations, environments)" ,
16
17
}
17
- cmd .AddCommand (resourceTop )
18
+ cmd .AddCommand (resourceTop () )
18
19
return cmd
19
20
}
20
21
21
- var resourceTop = & cobra.Command {
22
- Use :"top" ,
23
- RunE :func (cmd * cobra.Command ,args []string )error {
24
- ctx := cmd .Context ()
25
-
26
- client ,err := newClient ()
27
- if err != nil {
28
- return err
29
- }
30
-
31
- envs ,err := client .ListEnvironments (ctx )
32
- if err != nil {
33
- return err
34
- }
35
-
36
- userEnvs := make (map [string ][]coder.Environment )
37
- for _ ,e := range envs {
38
- userEnvs [e .UserID ]= append (userEnvs [e .UserID ],e )
39
- }
40
-
41
- users ,err := client .Users (ctx )
42
- if err != nil {
43
- return err
44
- }
45
-
46
- orgs := make (map [string ]coder.Organization )
47
- orglist ,err := client .Organizations (ctx )
48
- if err != nil {
49
- return err
50
- }
51
- for _ ,o := range orglist {
52
- orgs [o .ID ]= o
53
- }
54
-
55
- tabwriter := tabwriter .NewWriter (os .Stdout ,0 ,0 ,4 ,' ' ,0 )
56
- for _ ,u := range users {
57
- _ ,_ = fmt .Fprintf (tabwriter ,"%s\t (%s)\t %s" ,u .Name ,u .Email ,aggregateEnvResources (userEnvs [u .ID ]))
58
- if len (userEnvs [u .ID ])> 0 {
59
- _ ,_ = fmt .Fprintf (tabwriter ,"\f " )
22
+ func resourceTop ()* cobra.Command {
23
+ cmd := & cobra.Command {
24
+ Use :"top" ,
25
+ RunE :func (cmd * cobra.Command ,args []string )error {
26
+ ctx := cmd .Context ()
27
+
28
+ client ,err := newClient ()
29
+ if err != nil {
30
+ return err
31
+ }
32
+
33
+ envs ,err := client .ListEnvironments (ctx )
34
+ if err != nil {
35
+ return err
60
36
}
61
- for _ ,env := range userEnvs [u .ID ] {
62
- _ ,_ = fmt .Fprintf (tabwriter ,"\t " )
63
- _ ,_ = fmt .Fprintln (tabwriter ,fmtEnvResources (env ,orgs ))
37
+
38
+ userEnvs := make (map [string ][]coder.Environment )
39
+ for _ ,e := range envs {
40
+ userEnvs [e .UserID ]= append (userEnvs [e .UserID ],e )
64
41
}
65
- fmt .Fprint (tabwriter ,"\n " )
66
- }
67
- _ = tabwriter .Flush ()
68
42
69
- return nil
70
- },
43
+ users ,err := client .Users (ctx )
44
+ if err != nil {
45
+ return err
46
+ }
47
+
48
+ orgs := make (map [string ]coder.Organization )
49
+ orglist ,err := client .Organizations (ctx )
50
+ if err != nil {
51
+ return err
52
+ }
53
+ for _ ,o := range orglist {
54
+ orgs [o .ID ]= o
55
+ }
56
+
57
+ tabwriter := tabwriter .NewWriter (os .Stdout ,0 ,0 ,4 ,' ' ,0 )
58
+ var userResources []aggregatedUser
59
+ for _ ,u := range users {
60
+ // truncate user names to ensure tabwriter doesn't push our entire table too far
61
+ u .Name = truncate (u .Name ,20 ,"..." )
62
+ userResources = append (userResources ,aggregatedUser {User :u ,resources :aggregateEnvResources (userEnvs [u .ID ])})
63
+ }
64
+ sort .Slice (userResources ,func (i ,j int )bool {
65
+ return userResources [i ].cpuAllocation > userResources [j ].cpuAllocation
66
+ })
67
+
68
+ for _ ,u := range userResources {
69
+ _ ,_ = fmt .Fprintf (tabwriter ,"%s\t (%s)\t %s" ,u .Name ,u .Email ,u .resources )
70
+ if verbose {
71
+ if len (userEnvs [u .ID ])> 0 {
72
+ _ ,_ = fmt .Fprintf (tabwriter ,"\f " )
73
+ }
74
+ for _ ,env := range userEnvs [u .ID ] {
75
+ _ ,_ = fmt .Fprintf (tabwriter ,"\t " )
76
+ _ ,_ = fmt .Fprintln (tabwriter ,fmtEnvResources (env ,orgs ))
77
+ }
78
+ }
79
+ fmt .Fprint (tabwriter ,"\n " )
80
+ }
81
+ _ = tabwriter .Flush ()
82
+
83
+ return nil
84
+ },
85
+ }
86
+
87
+ return cmd
88
+ }
89
+
90
+ type aggregatedUser struct {
91
+ coder.User
92
+ resources
71
93
}
72
94
73
95
func resourcesFromEnv (env coder.Environment )resources {
@@ -104,3 +126,11 @@ type resources struct {
104
126
func (a resources )String ()string {
105
127
return fmt .Sprintf ("[cpu: alloc=%.1fvCPU, util=%.1f]\t [mem: alloc=%.1fGB, util=%.1f]" ,a .cpuAllocation ,a .cpuUtilization ,a .memAllocation ,a .memUtilization )
106
128
}
129
+
130
+ // truncate the given string and replace the removed chars with some replacement (ex: "...")
131
+ func truncate (str string ,max int ,replace string )string {
132
+ if len (str )<= max {
133
+ return str
134
+ }
135
+ return str [:max + 1 ]+ replace
136
+ }