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

Commitd11afcd

Browse files
authored
Merge pull request#142 from cdr/resource-filters
Add resource top flags for filtering and sorting
2 parentsd9cbba1 +1185a2f commitd11afcd

File tree

2 files changed

+157
-75
lines changed

2 files changed

+157
-75
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
envsCommand(),
2828
makeSyncCmd(),
2929
makeURLCmd(),
30-
makeResourceCmd(),
30+
resourceCmd(),
3131
completionCmd,
3232
genDocs(app),
3333
)

‎internal/cmd/resourcemanager.go

Lines changed: 156 additions & 74 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,124 @@ 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+
Short:"resource viewer with Coder platform annotations",
40+
RunE:runResourceTop(&options),
41+
Example:`coder resources top --group org
42+
coder resources top --group org --verbose --org DevOps
43+
coder resources top --group user --verbose --user name@example.com
44+
coder resources top --sort-by memory --show-empty`,
45+
}
46+
cmd.Flags().StringVar(&options.group,"group","user","the grouping parameter (user|org)")
47+
cmd.Flags().StringVar(&options.user,"user","","filter by a user email")
48+
cmd.Flags().StringVar(&options.org,"org","","filter by the name of an organization")
49+
cmd.Flags().StringVar(&options.sortBy,"sort-by","cpu","field to sort aggregate groups and environments by (cpu|memory)")
50+
cmd.Flags().BoolVar(&options.showEmptyGroups,"show-empty",false,"show groups with zero active environments")
3551

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-
}
52+
returncmd
53+
}
4954

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

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

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-
}
77+
users,err:=client.Users(ctx)
78+
iferr!=nil {
79+
returnxerrors.Errorf("get users: %w",err)
80+
}
81+
82+
orgs,err:=client.Organizations(ctx)
83+
iferr!=nil {
84+
returnxerrors.Errorf("get organizations: %w",err)
85+
}
9286

93-
printResourceTop(os.Stdout,groups,labeler)
94-
returnnil
95-
},
87+
vargroups []groupable
88+
varlabelerenvLabeler
89+
switchoptions.group {
90+
case"user":
91+
groups,labeler=aggregateByUser(users,orgs,envs,*options)
92+
case"org":
93+
groups,labeler=aggregateByOrg(users,orgs,envs,*options)
94+
default:
95+
returnxerrors.Errorf("unknown --group %q",options.group)
96+
}
97+
98+
returnprintResourceTop(os.Stdout,groups,labeler,options.showEmptyGroups,options.sortBy)
9699
}
97-
cmd.Flags().StringVar(&group,"group","user","the grouping parameter (user|org)")
100+
}
98101

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

102146
// groupable specifies a structure capable of being an aggregation group of environments (user, org, all)
@@ -135,20 +179,24 @@ func (o orgGrouping) header() string {
135179
returnfmt.Sprintf("%s\t(%v member%s)",truncate(o.org.Name,20,"..."),len(o.org.Members),plural)
136180
}
137181

138-
funcprintResourceTop(writer io.Writer,groups []groupable,labelerenvLabeler) {
182+
funcprintResourceTop(writer io.Writer,groups []groupable,labelerenvLabeler,showEmptyGroupsbool,sortBystring)error {
139183
tabwriter:=tabwriter.NewWriter(writer,0,0,4,' ',0)
140184
deferfunc() {_=tabwriter.Flush() }()
141185

142186
varuserResources []aggregatedResources
143187
for_,group:=rangegroups {
144-
userResources=append(
145-
userResources,
146-
aggregatedResources{groupable:group,resources:aggregateEnvResources(group.environments())},
147-
)
188+
if!showEmptyGroups&&len(group.environments())<1 {
189+
continue
190+
}
191+
userResources=append(userResources,aggregatedResources{
192+
groupable:group,resources:aggregateEnvResources(group.environments()),
193+
})
194+
}
195+
196+
err:=sortAggregatedResources(userResources,sortBy)
197+
iferr!=nil {
198+
returnerr
148199
}
149-
sort.Slice(userResources,func(i,jint)bool {
150-
returnuserResources[i].cpuAllocation>userResources[j].cpuAllocation
151-
})
152200

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

168250
typeaggregatedResourcesstruct {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp