@@ -23,6 +23,7 @@ func makeResourceCmd() *cobra.Command {
23
23
}
24
24
25
25
func resourceTop () * cobra.Command {
26
+ var group string
26
27
cmd := & cobra.Command {
27
28
Use : "top" ,
28
29
RunE : func (cmd * cobra.Command , args []string ) error {
@@ -39,64 +40,124 @@ func resourceTop() *cobra.Command {
39
40
return xerrors .Errorf ("get environments %w" , err )
40
41
}
41
42
42
- userEnvs := make (map [string ][]coder.Environment )
43
- for _ , e := range envs {
44
- userEnvs [e .UserID ] = append (userEnvs [e .UserID ], e )
45
- }
46
-
47
43
users , err := client .Users (ctx )
48
44
if err != nil {
49
45
return xerrors .Errorf ("get users: %w" , err )
50
46
}
51
47
52
- orgIDMap := make (map [string ]coder.Organization )
53
- orglist , err := client .Organizations (ctx )
48
+ orgs , err := client .Organizations (ctx )
54
49
if err != nil {
55
50
return xerrors .Errorf ("get organizations: %w" , err )
56
51
}
57
- for _ , o := range orglist {
58
- orgIDMap [o .ID ] = o
52
+
53
+ var groups []groupable
54
+ var labeler envLabeler
55
+ switch group {
56
+ case "user" :
57
+ userEnvs := make (map [string ][]coder.Environment , len (users ))
58
+ for _ , e := range envs {
59
+ userEnvs [e .UserID ] = append (userEnvs [e .UserID ], e )
60
+ }
61
+ for _ , u := range users {
62
+ groups = append (groups , userGrouping {user : u , envs : userEnvs [u .ID ]})
63
+ }
64
+ orgIDMap := make (map [string ]coder.Organization )
65
+ for _ , o := range orgs {
66
+ orgIDMap [o .ID ] = o
67
+ }
68
+ labeler = orgLabeler {orgIDMap }
69
+ case "org" :
70
+ orgEnvs := make (map [string ][]coder.Environment , len (orgs ))
71
+ for _ , e := range envs {
72
+ orgEnvs [e .OrganizationID ] = append (orgEnvs [e .OrganizationID ], e )
73
+ }
74
+ for _ , o := range orgs {
75
+ groups = append (groups , orgGrouping {org : o , envs : orgEnvs [o .ID ]})
76
+ }
77
+ userIDMap := make (map [string ]coder.User )
78
+ for _ , u := range users {
79
+ userIDMap [u .ID ] = u
80
+ }
81
+ labeler = userLabeler {userIDMap }
82
+ default :
83
+ return xerrors .Errorf ("unknown --group %q" , group )
59
84
}
60
85
61
- printResourceTop (os .Stdout , users , orgIDMap , userEnvs )
86
+ printResourceTop (os .Stdout , groups , labeler )
62
87
return nil
63
88
},
64
89
}
90
+ cmd .Flags ().StringVar (& group , "group" , "user" , "the grouping parameter (user|org|all)" )
65
91
66
92
return cmd
67
93
}
68
94
69
- func printResourceTop (writer io.Writer , users []coder.User , orgIDMap map [string ]coder.Organization , userEnvs map [string ][]coder.Environment ) {
95
+ // groupable specifies a structure capable of being an aggregation group of environments (user, org, all)
96
+ type groupable interface {
97
+ header () string
98
+ environments () []coder.Environment
99
+ }
100
+
101
+ type userGrouping struct {
102
+ user coder.User
103
+ envs []coder.Environment
104
+ }
105
+
106
+ func (u userGrouping ) environments () []coder.Environment {
107
+ return u .envs
108
+ }
109
+
110
+ func (u userGrouping ) header () string {
111
+ return fmt .Sprintf ("%s\t (%s)" , truncate (u .user .Name , 20 , "..." ), u .user .Email )
112
+ }
113
+
114
+ type orgGrouping struct {
115
+ org coder.Organization
116
+ envs []coder.Environment
117
+ }
118
+
119
+ func (o orgGrouping ) environments () []coder.Environment {
120
+ return o .envs
121
+ }
122
+
123
+ func (o orgGrouping ) header () string {
124
+ plural := "s"
125
+ if len (o .org .Members ) < 2 {
126
+ plural = ""
127
+ }
128
+ return fmt .Sprintf ("%s\t (%v member%s)" , truncate (o .org .Name , 20 , "..." ), len (o .org .Members ), plural )
129
+ }
130
+
131
+ func printResourceTop (writer io.Writer , groups []groupable , labeler envLabeler ) {
70
132
tabwriter := tabwriter .NewWriter (writer , 0 , 0 , 4 , ' ' , 0 )
71
133
defer func () { _ = tabwriter .Flush () }()
72
134
73
- var userResources []aggregatedUser
74
- for _ , u := range users {
135
+ var userResources []aggregatedResources
136
+ for _ , group := range groups {
75
137
// truncate user names to ensure tabwriter doesn't push our entire table too far
76
- u .Name = truncate (u .Name , 20 , "..." )
77
- userResources = append (userResources , aggregatedUser {User : u , resources : aggregateEnvResources (userEnvs [u .ID ])})
138
+ userResources = append (userResources , aggregatedResources {groupable : group , resources : aggregateEnvResources (group .environments ())})
78
139
}
79
140
sort .Slice (userResources , func (i , j int ) bool {
80
141
return userResources [i ].cpuAllocation > userResources [j ].cpuAllocation
81
142
})
82
143
83
144
for _ , u := range userResources {
84
- _ , _ = fmt .Fprintf (tabwriter , "%s\t (%s) \t %s " , u .Name , u . Email , u .resources )
145
+ _ , _ = fmt .Fprintf (tabwriter , "%s\t %s " , u .header () , u .resources )
85
146
if verbose {
86
- if len (userEnvs [ u . ID ] ) > 0 {
147
+ if len (u . environments () ) > 0 {
87
148
_ , _ = fmt .Fprintf (tabwriter , "\f " )
88
149
}
89
- for _ , env := range userEnvs [ u . ID ] {
150
+ for _ , env := range u . environments () {
90
151
_ , _ = fmt .Fprintf (tabwriter , "\t " )
91
- _ , _ = fmt .Fprintln (tabwriter , fmtEnvResources (env , orgIDMap ))
152
+ _ , _ = fmt .Fprintln (tabwriter , fmtEnvResources (env , labeler ))
92
153
}
93
154
}
94
155
_ , _ = fmt .Fprint (tabwriter , "\n " )
95
156
}
96
157
}
97
158
98
- type aggregatedUser struct {
99
- coder. User
159
+ type aggregatedResources struct {
160
+ groupable
100
161
resources
101
162
}
102
163
@@ -109,8 +170,28 @@ func resourcesFromEnv(env coder.Environment) resources {
109
170
}
110
171
}
111
172
112
- func fmtEnvResources (env coder.Environment , orgs map [string ]coder.Organization ) string {
113
- return fmt .Sprintf ("%s\t %s\t [org: %s]" , env .Name , resourcesFromEnv (env ), orgs [env .OrganizationID ].Name )
173
+ func fmtEnvResources (env coder.Environment , labeler envLabeler ) string {
174
+ return fmt .Sprintf ("%s\t %s\t %s" , env .Name , resourcesFromEnv (env ), labeler .label (env ))
175
+ }
176
+
177
+ type envLabeler interface {
178
+ label (coder.Environment ) string
179
+ }
180
+
181
+ type orgLabeler struct {
182
+ orgMap map [string ]coder.Organization
183
+ }
184
+
185
+ func (o orgLabeler ) label (e coder.Environment ) string {
186
+ return fmt .Sprintf ("[org: %s]" , o .orgMap [e .OrganizationID ].Name )
187
+ }
188
+
189
+ type userLabeler struct {
190
+ userMap map [string ]coder.User
191
+ }
192
+
193
+ func (u userLabeler ) label (e coder.Environment ) string {
194
+ return fmt .Sprintf ("[user: %s]" , u .userMap [e .UserID ].Email )
114
195
}
115
196
116
197
func aggregateEnvResources (envs []coder.Environment ) resources {
0 commit comments