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

Commit 902d17f

Browse files
committed
Abstract env resource grouping and label properly
1 parent b3e8ec9 commit 902d17f

File tree

1 file changed

+104
-23
lines changed

1 file changed

+104
-23
lines changed

internal/cmd/resourcemanager.go

+104-23
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 {
@@ -39,64 +40,124 @@ func resourceTop() *cobra.Command {
3940
return xerrors.Errorf("get environments %w", err)
4041
}
4142

42-
userEnvs := make(map[string][]coder.Environment)
43-
for _, e := range envs {
44-
userEnvs[e.UserID] = append(userEnvs[e.UserID], e)
45-
}
46-
4743
users, err := client.Users(ctx)
4844
if err != nil {
4945
return xerrors.Errorf("get users: %w", err)
5046
}
5147

52-
orgIDMap := make(map[string]coder.Organization)
53-
orglist, err := client.Organizations(ctx)
48+
orgs, err := client.Organizations(ctx)
5449
if err != nil {
5550
return xerrors.Errorf("get organizations: %w", err)
5651
}
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)
5984
}
6085

61-
printResourceTop(os.Stdout, users, orgIDMap, userEnvs)
86+
printResourceTop(os.Stdout, groups, labeler)
6287
return nil
6388
},
6489
}
90+
cmd.Flags().StringVar(&group, "group", "user", "the grouping parameter (user|org|all)")
6591

6692
return cmd
6793
}
6894

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) {
70132
tabwriter := tabwriter.NewWriter(writer, 0, 0, 4, ' ', 0)
71133
defer func() { _ = tabwriter.Flush() }()
72134

73-
var userResources []aggregatedUser
74-
for _, u := range users {
135+
var userResources []aggregatedResources
136+
for _, group := range groups {
75137
// 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())})
78139
}
79140
sort.Slice(userResources, func(i, j int) bool {
80141
return userResources[i].cpuAllocation > userResources[j].cpuAllocation
81142
})
82143

83144
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)
85146
if verbose {
86-
if len(userEnvs[u.ID]) > 0 {
147+
if len(u.environments()) > 0 {
87148
_, _ = fmt.Fprintf(tabwriter, "\f")
88149
}
89-
for _, env := range userEnvs[u.ID] {
150+
for _, env := range u.environments() {
90151
_, _ = fmt.Fprintf(tabwriter, "\t")
91-
_, _ = fmt.Fprintln(tabwriter, fmtEnvResources(env, orgIDMap))
152+
_, _ = fmt.Fprintln(tabwriter, fmtEnvResources(env, labeler))
92153
}
93154
}
94155
_, _ = fmt.Fprint(tabwriter, "\n")
95156
}
96157
}
97158

98-
type aggregatedUser struct {
99-
coder.User
159+
type aggregatedResources struct {
160+
groupable
100161
resources
101162
}
102163

@@ -109,8 +170,28 @@ func resourcesFromEnv(env coder.Environment) resources {
109170
}
110171
}
111172

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)
114195
}
115196

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

0 commit comments

Comments
 (0)