Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 30, 2024. It is now read-only.
/coder-v1-cliPublic archive

Commit45e7865

Browse files
committed
Add resource top flags for filtering and sorting
1 parente5d55d0 commit45e7865

File tree

2 files changed

+152
-74
lines changed

2 files changed

+152
-74
lines changed

‎internal/cmd/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func Make() *cobra.Command {
2727
makeEnvsCommand(),
2828
makeSyncCmd(),
2929
makeURLCmd(),
30-
makeResourceCmd(),
30+
resourceCmd(),
3131
completionCmd,
3232
genDocs(app),
3333
)

‎internal/cmd/resourcemanager.go

Lines changed: 151 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import (
99

1010
"cdr.dev/coder-cli/coder-sdk"
1111
"github.com/spf13/cobra"
12+
"go.coder.com/flog"
1213
"golang.org/x/xerrors"
1314
)
1415

15-
funcmakeResourceCmd()*cobra.Command {
16+
funcresourceCmd()*cobra.Command {
1617
cmd:=&cobra.Command{
1718
Use:"resources",
1819
Short:"manage Coder resources with platform-level context (users, organizations, environments)",
@@ -22,81 +23,119 @@ func makeResourceCmd() *cobra.Command {
2223
returncmd
2324
}
2425

26+
typeresourceTopOptionsstruct {
27+
groupstring
28+
userstring
29+
orgstring
30+
sortBystring
31+
showEmptyGroupsbool
32+
}
33+
2534
funcresourceTop()*cobra.Command {
26-
vargroupstring
35+
varoptionsresourceTopOptions
36+
2737
cmd:=&cobra.Command{
28-
Use:"top",
29-
RunE:func(cmd*cobra.Command,args []string)error {
30-
ctx:=cmd.Context()
31-
client,err:=newClient()
32-
iferr!=nil {
33-
returnerr
34-
}
38+
Use:"top",
39+
RunE:runResourceTop(&options),
40+
}
41+
cmd.Flags().StringVar(&options.group,"group","user","the grouping parameter (user|org)")
42+
cmd.Flags().StringVar(&options.user,"user","","filter by a user email")
43+
cmd.Flags().StringVar(&options.org,"org","","filter by the name of an organization")
44+
cmd.Flags().StringVar(&options.sortBy,"sort-by","cpu","field to sort aggregate groups and environments by (cpu|memory)")
45+
cmd.Flags().BoolVar(&options.showEmptyGroups,"show-empty",false,"show groups with zero active environments")
3546

36-
// NOTE: it's not worth parrallelizing these calls yet given that this specific endpoint
37-
// takes about 20x times longer than the other two
38-
allEnvs,err:=client.Environments(ctx)
39-
iferr!=nil {
40-
returnxerrors.Errorf("get environments %w",err)
41-
}
42-
// only include environments whose last status was "ON"
43-
envs:=make([]coder.Environment,0)
44-
for_,e:=rangeallEnvs {
45-
ife.LatestStat.ContainerStatus==coder.EnvironmentOn {
46-
envs=append(envs,e)
47-
}
48-
}
47+
returncmd
48+
}
4949

50-
users,err:=client.Users(ctx)
51-
iferr!=nil {
52-
returnxerrors.Errorf("get users: %w",err)
53-
}
50+
funcrunResourceTop(options*resourceTopOptions)func(cmd*cobra.Command,args []string)error {
51+
returnfunc(cmd*cobra.Command,args []string)error {
52+
ctx:=cmd.Context()
53+
client,err:=newClient()
54+
iferr!=nil {
55+
returnerr
56+
}
5457

55-
orgs,err:=client.Organizations(ctx)
56-
iferr!=nil {
57-
returnxerrors.Errorf("get organizations: %w",err)
58+
// NOTE: it's not worth parrallelizing these calls yet given that this specific endpoint
59+
// takes about 20x times longer than the other two
60+
allEnvs,err:=client.Environments(ctx)
61+
iferr!=nil {
62+
returnxerrors.Errorf("get environments %w",err)
63+
}
64+
// only include environments whose last status was "ON"
65+
envs:=make([]coder.Environment,0)
66+
for_,e:=rangeallEnvs {
67+
ife.LatestStat.ContainerStatus==coder.EnvironmentOn {
68+
envs=append(envs,e)
5869
}
70+
}
5971

60-
vargroups []groupable
61-
varlabelerenvLabeler
62-
switchgroup {
63-
case"user":
64-
userEnvs:=make(map[string][]coder.Environment,len(users))
65-
for_,e:=rangeenvs {
66-
userEnvs[e.UserID]=append(userEnvs[e.UserID],e)
67-
}
68-
for_,u:=rangeusers {
69-
groups=append(groups,userGrouping{user:u,envs:userEnvs[u.ID]})
70-
}
71-
orgIDMap:=make(map[string]coder.Organization)
72-
for_,o:=rangeorgs {
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:=rangeenvs {
79-
orgEnvs[e.OrganizationID]=append(orgEnvs[e.OrganizationID],e)
80-
}
81-
for_,o:=rangeorgs {
82-
groups=append(groups,orgGrouping{org:o,envs:orgEnvs[o.ID]})
83-
}
84-
userIDMap:=make(map[string]coder.User)
85-
for_,u:=rangeusers {
86-
userIDMap[u.ID]=u
87-
}
88-
labeler=userLabeler{userIDMap}
89-
default:
90-
returnxerrors.Errorf("unknown --group %q",group)
91-
}
72+
users,err:=client.Users(ctx)
73+
iferr!=nil {
74+
returnxerrors.Errorf("get users: %w",err)
75+
}
9276

93-
printResourceTop(os.Stdout,groups,labeler)
94-
returnnil
95-
},
77+
orgs,err:=client.Organizations(ctx)
78+
iferr!=nil {
79+
returnxerrors.Errorf("get organizations: %w",err)
80+
}
81+
82+
vargroups []groupable
83+
varlabelerenvLabeler
84+
switchoptions.group {
85+
case"user":
86+
groups,labeler=aggregateByUser(users,orgs,envs,*options)
87+
case"org":
88+
groups,labeler=aggregateByOrg(users,orgs,envs,*options)
89+
default:
90+
returnxerrors.Errorf("unknown --group %q",options.group)
91+
}
92+
93+
returnprintResourceTop(os.Stdout,groups,labeler,options.showEmptyGroups,options.sortBy)
9694
}
97-
cmd.Flags().StringVar(&group,"group","user","the grouping parameter (user|org)")
95+
}
9896

99-
returncmd
97+
funcaggregateByUser(users []coder.User,orgs []coder.Organization,envs []coder.Environment,optionsresourceTopOptions) ([]groupable,envLabeler) {
98+
vargroups []groupable
99+
orgIDMap:=make(map[string]coder.Organization)
100+
for_,o:=rangeorgs {
101+
orgIDMap[o.ID]=o
102+
}
103+
userEnvs:=make(map[string][]coder.Environment,len(users))
104+
for_,e:=rangeenvs {
105+
ifoptions.org!=""&&orgIDMap[e.OrganizationID].Name!=options.org {
106+
continue
107+
}
108+
userEnvs[e.UserID]=append(userEnvs[e.UserID],e)
109+
}
110+
for_,u:=rangeusers {
111+
ifoptions.user!=""&&u.Email!=options.user {
112+
continue
113+
}
114+
groups=append(groups,userGrouping{user:u,envs:userEnvs[u.ID]})
115+
}
116+
returngroups,orgLabeler{orgIDMap}
117+
}
118+
119+
funcaggregateByOrg(users []coder.User,orgs []coder.Organization,envs []coder.Environment,optionsresourceTopOptions) ([]groupable,envLabeler) {
120+
vargroups []groupable
121+
userIDMap:=make(map[string]coder.User)
122+
for_,u:=rangeusers {
123+
userIDMap[u.ID]=u
124+
}
125+
orgEnvs:=make(map[string][]coder.Environment,len(orgs))
126+
for_,e:=rangeenvs {
127+
ifoptions.user!=""&&userIDMap[e.UserID].Email!=options.user {
128+
continue
129+
}
130+
orgEnvs[e.OrganizationID]=append(orgEnvs[e.OrganizationID],e)
131+
}
132+
for_,o:=rangeorgs {
133+
ifoptions.org!=""&&o.Name!=options.org {
134+
continue
135+
}
136+
groups=append(groups,orgGrouping{org:o,envs:orgEnvs[o.ID]})
137+
}
138+
returngroups,userLabeler{userIDMap}
100139
}
101140

102141
// groupable specifies a structure capable of being an aggregation group of environments (user, org, all)
@@ -135,20 +174,25 @@ func (o orgGrouping) header() string {
135174
returnfmt.Sprintf("%s\t(%v member%s)",truncate(o.org.Name,20,"..."),len(o.org.Members),plural)
136175
}
137176

138-
funcprintResourceTop(writer io.Writer,groups []groupable,labelerenvLabeler) {
177+
funcprintResourceTop(writer io.Writer,groups []groupable,labelerenvLabeler,showEmptyGroupsbool,sortBystring)error {
139178
tabwriter:=tabwriter.NewWriter(writer,0,0,4,' ',0)
140179
deferfunc() {_=tabwriter.Flush() }()
141180

142181
varuserResources []aggregatedResources
143182
for_,group:=rangegroups {
144-
userResources=append(
145-
userResources,
146-
aggregatedResources{groupable:group,resources:aggregateEnvResources(group.environments())},
183+
if!showEmptyGroups&&len(group.environments())<1 {
184+
continue
185+
}
186+
userResources=append(userResources,aggregatedResources{
187+
groupable:group,resources:aggregateEnvResources(group.environments()),
188+
},
147189
)
148190
}
149-
sort.Slice(userResources,func(i,jint)bool {
150-
returnuserResources[i].cpuAllocation>userResources[j].cpuAllocation
151-
})
191+
192+
err:=sortAggregatedResources(userResources,sortBy)
193+
iferr!=nil {
194+
returnerr
195+
}
152196

153197
for_,u:=rangeuserResources {
154198
_,_=fmt.Fprintf(tabwriter,"%s\t%s",u.header(),u.resources)
@@ -163,6 +207,40 @@ func printResourceTop(writer io.Writer, groups []groupable, labeler envLabeler)
163207
}
164208
_,_=fmt.Fprint(tabwriter,"\n")
165209
}
210+
iflen(userResources)==0 {
211+
flog.Info("No groups for the given filters exist with active environments.")
212+
flog.Info("Use\"--show-empty\" to see groups with no resources.")
213+
}
214+
returnnil
215+
}
216+
217+
funcsortAggregatedResources(resources []aggregatedResources,sortBystring)error {
218+
constcpu="cpu"
219+
constmemory="memory"
220+
switchsortBy {
221+
casecpu:
222+
sort.Slice(resources,func(i,jint)bool {
223+
returnresources[i].cpuAllocation>resources[j].cpuAllocation
224+
})
225+
casememory:
226+
sort.Slice(resources,func(i,jint)bool {
227+
returnresources[i].memAllocation>resources[j].memAllocation
228+
})
229+
default:
230+
returnxerrors.Errorf("unknown --sort-by value of\"%s\"",sortBy)
231+
}
232+
for_,group:=rangeresources {
233+
envs:=group.environments()
234+
switchsortBy {
235+
casecpu:
236+
sort.Slice(envs,func(i,jint)bool {returnenvs[i].CPUCores>envs[j].CPUCores })
237+
casememory:
238+
sort.Slice(envs,func(i,jint)bool {returnenvs[i].MemoryGB>envs[j].MemoryGB })
239+
default:
240+
returnxerrors.Errorf("unknown --sort-by value of\"%s\"",sortBy)
241+
}
242+
}
243+
returnnil
166244
}
167245

168246
typeaggregatedResourcesstruct {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp