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

Commit eff753a

Browse files
committed
Abstract env resource grouping and label properly
1 parent af2ac67 commit eff753a

File tree

1 file changed

+111
-23
lines changed

1 file changed

+111
-23
lines changed

internal/cmd/resourcemanager.go

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func makeResourceCmd() *cobra.Command {
2323
}
2424

2525
func resourceTop() *cobra.Command {
26+
var group string
2627
cmd := &cobra.Command{
2728
Use: "top",
2829
RunE: func(cmd *cobra.Command, args []string) error {
@@ -34,69 +35,136 @@ func resourceTop() *cobra.Command {
3435

3536
// NOTE: it's not worth parrallelizing these calls yet given that this specific endpoint
3637
// takes about 20x times longer than the other two
37-
envs, err := client.Environments(ctx)
38+
allEnvs, err := client.Environments(ctx)
3839
if err != nil {
3940
return xerrors.Errorf("get environments %w", err)
4041
}
41-
42-
userEnvs := make(map[string][]coder.Environment)
43-
for _, e := range envs {
44-
userEnvs[e.UserID] = append(userEnvs[e.UserID], e)
42+
// only include environments whose last status was "ON"
43+
envs := make([]coder.Environment, 0)
44+
for _, e := range allEnvs {
45+
if e.LatestStat.ContainerStatus == coder.EnvironmentOn {
46+
envs = append(envs, e)
47+
}
4548
}
4649

4750
users, err := client.Users(ctx)
4851
if err != nil {
4952
return xerrors.Errorf("get users: %w", err)
5053
}
5154

52-
orgIDMap := make(map[string]coder.Organization)
53-
orglist, err := client.Organizations(ctx)
55+
orgs, err := client.Organizations(ctx)
5456
if err != nil {
5557
return xerrors.Errorf("get organizations: %w", err)
5658
}
57-
for _, o := range orglist {
58-
orgIDMap[o.ID] = o
59+
60+
var groups []groupable
61+
var labeler envLabeler
62+
switch group {
63+
case "user":
64+
userEnvs := make(map[string][]coder.Environment, len(users))
65+
for _, e := range envs {
66+
userEnvs[e.UserID] = append(userEnvs[e.UserID], e)
67+
}
68+
for _, u := range users {
69+
groups = append(groups, userGrouping{user: u, envs: userEnvs[u.ID]})
70+
}
71+
orgIDMap := make(map[string]coder.Organization)
72+
for _, o := range orgs {
73+
orgIDMap[o.ID] = o
74+
}
75+
labeler = orgLabeler{orgIDMap}
76+
case "org":
77+
orgEnvs := make(map[string][]coder.Environment, len(orgs))
78+
for _, e := range envs {
79+
orgEnvs[e.OrganizationID] = append(orgEnvs[e.OrganizationID], e)
80+
}
81+
for _, o := range orgs {
82+
groups = append(groups, orgGrouping{org: o, envs: orgEnvs[o.ID]})
83+
}
84+
userIDMap := make(map[string]coder.User)
85+
for _, u := range users {
86+
userIDMap[u.ID] = u
87+
}
88+
labeler = userLabeler{userIDMap}
89+
default:
90+
return xerrors.Errorf("unknown --group %q", group)
5991
}
6092

61-
printResourceTop(os.Stdout, users, orgIDMap, userEnvs)
93+
printResourceTop(os.Stdout, groups, labeler)
6294
return nil
6395
},
6496
}
97+
cmd.Flags().StringVar(&group, "group", "user", "the grouping parameter (user|org)")
6598

6699
return cmd
67100
}
68101

69-
func printResourceTop(writer io.Writer, users []coder.User, orgIDMap map[string]coder.Organization, userEnvs map[string][]coder.Environment) {
102+
// groupable specifies a structure capable of being an aggregation group of environments (user, org, all)
103+
type groupable interface {
104+
header() string
105+
environments() []coder.Environment
106+
}
107+
108+
type userGrouping struct {
109+
user coder.User
110+
envs []coder.Environment
111+
}
112+
113+
func (u userGrouping) environments() []coder.Environment {
114+
return u.envs
115+
}
116+
117+
func (u userGrouping) header() string {
118+
return fmt.Sprintf("%s\t(%s)", truncate(u.user.Name, 20, "..."), u.user.Email)
119+
}
120+
121+
type orgGrouping struct {
122+
org coder.Organization
123+
envs []coder.Environment
124+
}
125+
126+
func (o orgGrouping) environments() []coder.Environment {
127+
return o.envs
128+
}
129+
130+
func (o orgGrouping) header() string {
131+
plural := "s"
132+
if len(o.org.Members) < 2 {
133+
plural = ""
134+
}
135+
return fmt.Sprintf("%s\t(%v member%s)", truncate(o.org.Name, 20, "..."), len(o.org.Members), plural)
136+
}
137+
138+
func printResourceTop(writer io.Writer, groups []groupable, labeler envLabeler) {
70139
tabwriter := tabwriter.NewWriter(writer, 0, 0, 4, ' ', 0)
71140
defer func() { _ = tabwriter.Flush() }()
72141

73-
var userResources []aggregatedUser
74-
for _, u := range users {
142+
var userResources []aggregatedResources
143+
for _, group := range groups {
75144
// 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])})
145+
userResources = append(userResources, aggregatedResources{groupable: group, resources: aggregateEnvResources(group.environments())})
78146
}
79147
sort.Slice(userResources, func(i, j int) bool {
80148
return userResources[i].cpuAllocation > userResources[j].cpuAllocation
81149
})
82150

83151
for _, u := range userResources {
84-
_, _ = fmt.Fprintf(tabwriter, "%s\t(%s)\t%s", u.Name, u.Email, u.resources)
152+
_, _ = fmt.Fprintf(tabwriter, "%s\t%s", u.header(), u.resources)
85153
if verbose {
86-
if len(userEnvs[u.ID]) > 0 {
154+
if len(u.environments()) > 0 {
87155
_, _ = fmt.Fprintf(tabwriter, "\f")
88156
}
89-
for _, env := range userEnvs[u.ID] {
157+
for _, env := range u.environments() {
90158
_, _ = fmt.Fprintf(tabwriter, "\t")
91-
_, _ = fmt.Fprintln(tabwriter, fmtEnvResources(env, orgIDMap))
159+
_, _ = fmt.Fprintln(tabwriter, fmtEnvResources(env, labeler))
92160
}
93161
}
94162
_, _ = fmt.Fprint(tabwriter, "\n")
95163
}
96164
}
97165

98-
type aggregatedUser struct {
99-
coder.User
166+
type aggregatedResources struct {
167+
groupable
100168
resources
101169
}
102170

@@ -109,8 +177,28 @@ func resourcesFromEnv(env coder.Environment) resources {
109177
}
110178
}
111179

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)
180+
func fmtEnvResources(env coder.Environment, labeler envLabeler) string {
181+
return fmt.Sprintf("%s\t%s\t%s", env.Name, resourcesFromEnv(env), labeler.label(env))
182+
}
183+
184+
type envLabeler interface {
185+
label(coder.Environment) string
186+
}
187+
188+
type orgLabeler struct {
189+
orgMap map[string]coder.Organization
190+
}
191+
192+
func (o orgLabeler) label(e coder.Environment) string {
193+
return fmt.Sprintf("[org: %s]", o.orgMap[e.OrganizationID].Name)
194+
}
195+
196+
type userLabeler struct {
197+
userMap map[string]coder.User
198+
}
199+
200+
func (u userLabeler) label(e coder.Environment) string {
201+
return fmt.Sprintf("[user: %s]", u.userMap[e.UserID].Email)
114202
}
115203

116204
func aggregateEnvResources(envs []coder.Environment) resources {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy