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

Commit9cbe2b2

Browse files
authored
chore: create workspaces and templates for multiple orgs (#13866)
* chore: creating workspaces and templates to work with orgs* handle wrong org selected* create org member in coderdtest helper
1 parente4aef27 commit9cbe2b2

File tree

9 files changed

+312
-16
lines changed

9 files changed

+312
-16
lines changed

‎cli/create.go

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"io"
7+
"strings"
78
"time"
89

910
"github.com/google/uuid"
@@ -29,7 +30,9 @@ func (r *RootCmd) create() *serpent.Command {
2930
parameterFlagsworkspaceParameterFlags
3031
autoUpdatesstring
3132
copyParametersFromstring
32-
orgContext=NewOrganizationContext()
33+
// Organization context is only required if more than 1 template
34+
// shares the same name across multiple organizations.
35+
orgContext=NewOrganizationContext()
3336
)
3437
client:=new(codersdk.Client)
3538
cmd:=&serpent.Command{
@@ -44,11 +47,7 @@ func (r *RootCmd) create() *serpent.Command {
4447
),
4548
Middleware:serpent.Chain(r.InitClient(client)),
4649
Handler:func(inv*serpent.Invocation)error {
47-
organization,err:=orgContext.Selected(inv,client)
48-
iferr!=nil {
49-
returnerr
50-
}
51-
50+
varerrerror
5251
workspaceOwner:=codersdk.Me
5352
iflen(inv.Args)>=1 {
5453
workspaceOwner,workspaceName,err=splitNamedWorkspace(inv.Args[0])
@@ -99,7 +98,7 @@ func (r *RootCmd) create() *serpent.Command {
9998
iftemplateName=="" {
10099
_,_=fmt.Fprintln(inv.Stdout,pretty.Sprint(cliui.DefaultStyles.Wrap,"Select a template below to preview the provisioned infrastructure:"))
101100

102-
templates,err:=client.TemplatesByOrganization(inv.Context(),organization.ID)
101+
templates,err:=client.Templates(inv.Context(),codersdk.TemplateFilter{})
103102
iferr!=nil {
104103
returnerr
105104
}
@@ -111,13 +110,28 @@ func (r *RootCmd) create() *serpent.Command {
111110
templateNames:=make([]string,0,len(templates))
112111
templateByName:=make(map[string]codersdk.Template,len(templates))
113112

113+
// If more than 1 organization exists in the list of templates,
114+
// then include the organization name in the select options.
115+
uniqueOrganizations:=make(map[uuid.UUID]bool)
116+
for_,template:=rangetemplates {
117+
uniqueOrganizations[template.OrganizationID]=true
118+
}
119+
114120
for_,template:=rangetemplates {
115121
templateName:=template.Name
122+
iflen(uniqueOrganizations)>1 {
123+
templateName+=cliui.Placeholder(
124+
fmt.Sprintf(
125+
" (%s)",
126+
template.OrganizationName,
127+
),
128+
)
129+
}
116130

117131
iftemplate.ActiveUserCount>0 {
118132
templateName+=cliui.Placeholder(
119133
fmt.Sprintf(
120-
"(used by %s)",
134+
" used by %s",
121135
formatActiveDevelopers(template.ActiveUserCount),
122136
),
123137
)
@@ -145,13 +159,65 @@ func (r *RootCmd) create() *serpent.Command {
145159
}
146160
templateVersionID=sourceWorkspace.LatestBuild.TemplateVersionID
147161
}else {
148-
template,err=client.TemplateByName(inv.Context(),organization.ID,templateName)
162+
templates,err:=client.Templates(inv.Context(), codersdk.TemplateFilter{
163+
ExactName:templateName,
164+
})
149165
iferr!=nil {
150166
returnxerrors.Errorf("get template by name: %w",err)
151167
}
168+
iflen(templates)==0 {
169+
returnxerrors.Errorf("no template found with the name %q",templateName)
170+
}
171+
172+
iflen(templates)>1 {
173+
templateOrgs:= []string{}
174+
for_,tpl:=rangetemplates {
175+
templateOrgs=append(templateOrgs,tpl.OrganizationName)
176+
}
177+
178+
selectedOrg,err:=orgContext.Selected(inv,client)
179+
iferr!=nil {
180+
returnxerrors.Errorf("multiple templates found with the name %q, use `--org=<organization_name>` to specify which template by that name to use. Organizations available: %s",templateName,strings.Join(templateOrgs,", "))
181+
}
182+
183+
index:=slices.IndexFunc(templates,func(i codersdk.Template)bool {
184+
returni.OrganizationID==selectedOrg.ID
185+
})
186+
ifindex==-1 {
187+
returnxerrors.Errorf("no templates found with the name %q in the organization %q. Templates by that name exist in organizations: %s. Use --org=<organization_name> to select one.",templateName,selectedOrg.Name,strings.Join(templateOrgs,", "))
188+
}
189+
190+
// remake the list with the only template selected
191+
templates= []codersdk.Template{templates[index]}
192+
}
193+
194+
template=templates[0]
152195
templateVersionID=template.ActiveVersionID
153196
}
154197

198+
// If the user specified an organization via a flag or env var, the template **must**
199+
// be in that organization. Otherwise, we should throw an error.
200+
orgValue,orgValueSource:=orgContext.ValueSource(inv)
201+
iforgValue!=""&&!(orgValueSource==serpent.ValueSourceDefault||orgValueSource==serpent.ValueSourceNone) {
202+
selectedOrg,err:=orgContext.Selected(inv,client)
203+
iferr!=nil {
204+
returnerr
205+
}
206+
207+
iftemplate.OrganizationID!=selectedOrg.ID {
208+
orgNameFormat:="'--org=%q'"
209+
iforgValueSource==serpent.ValueSourceEnv {
210+
orgNameFormat="CODER_ORGANIZATION=%q"
211+
}
212+
213+
returnxerrors.Errorf("template is in organization %q, but %s was specified. Use %s to use this template",
214+
template.OrganizationName,
215+
fmt.Sprintf(orgNameFormat,selectedOrg.Name),
216+
fmt.Sprintf(orgNameFormat,template.OrganizationName),
217+
)
218+
}
219+
}
220+
155221
varschedSpec*string
156222
ifstartAt!="" {
157223
sched,err:=parseCLISchedule(startAt)
@@ -207,7 +273,7 @@ func (r *RootCmd) create() *serpent.Command {
207273
ttlMillis=ptr.Ref(stopAfter.Milliseconds())
208274
}
209275

210-
workspace,err:=client.CreateWorkspace(inv.Context(),organization.ID,workspaceOwner, codersdk.CreateWorkspaceRequest{
276+
workspace,err:=client.CreateWorkspace(inv.Context(),template.OrganizationID,workspaceOwner, codersdk.CreateWorkspaceRequest{
211277
TemplateVersionID:templateVersionID,
212278
Name:workspaceName,
213279
AutostartSchedule:schedSpec,

‎cli/root.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,10 @@ func NewOrganizationContext() *OrganizationContext {
641641
return&OrganizationContext{}
642642
}
643643

644+
func (*OrganizationContext)optionName()string {return"Organization" }
644645
func (o*OrganizationContext)AttachOptions(cmd*serpent.Command) {
645646
cmd.Options=append(cmd.Options, serpent.Option{
646-
Name:"Organization",
647+
Name:o.optionName(),
647648
Description:"Select which organization (uuid or name) to use.",
648649
// Only required if the user is a part of more than 1 organization.
649650
// Otherwise, we can assume a default value.
@@ -655,6 +656,14 @@ func (o *OrganizationContext) AttachOptions(cmd *serpent.Command) {
655656
})
656657
}
657658

659+
func (o*OrganizationContext)ValueSource(inv*serpent.Invocation) (string, serpent.ValueSource) {
660+
opt:=inv.Command.Options.ByName(o.optionName())
661+
ifopt==nil {
662+
returno.FlagSelect,serpent.ValueSourceNone
663+
}
664+
returno.FlagSelect,opt.ValueSource
665+
}
666+
658667
func (o*OrganizationContext)Selected(inv*serpent.Invocation,client*codersdk.Client) (codersdk.Organization,error) {
659668
// Fetch the set of organizations the user is a member of.
660669
orgs,err:=client.OrganizationsByUser(inv.Context(),codersdk.Me)

‎cli/templatecreate.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (r *RootCmd) templateCreate() *serpent.Command {
160160
RequireActiveVersion:requireActiveVersion,
161161
}
162162

163-
_,err=client.CreateTemplate(inv.Context(),organization.ID,createReq)
163+
template,err:=client.CreateTemplate(inv.Context(),organization.ID,createReq)
164164
iferr!=nil {
165165
returnerr
166166
}
@@ -171,7 +171,7 @@ func (r *RootCmd) templateCreate() *serpent.Command {
171171
pretty.Sprint(cliui.DefaultStyles.DateTimeStamp,time.Now().Format(time.Stamp))+"! "+
172172
"Developers can provision a workspace with this template using:")+"\n")
173173

174-
_,_=fmt.Fprintln(inv.Stdout," "+pretty.Sprint(cliui.DefaultStyles.Code,fmt.Sprintf("coder create --template=%q [workspace name]",templateName)))
174+
_,_=fmt.Fprintln(inv.Stdout," "+pretty.Sprint(cliui.DefaultStyles.Code,fmt.Sprintf("coder create --template=%q--org=%q[workspace name]",templateName,template.OrganizationName)))
175175
_,_=fmt.Fprintln(inv.Stdout)
176176

177177
returnnil
@@ -244,6 +244,7 @@ func (r *RootCmd) templateCreate() *serpent.Command {
244244

245245
cliui.SkipPromptOption(),
246246
}
247+
orgContext.AttachOptions(cmd)
247248
cmd.Options=append(cmd.Options,uploadFlags.options()...)
248249
returncmd
249250
}

‎cli/testdata/coder_templates_create_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ USAGE:
77
flag
88

99
OPTIONS:
10+
-O, --org string, $CODER_ORGANIZATION
11+
Select which organization (uuid or name) to use.
12+
1013
--default-ttl duration (default: 24h)
1114
Specify a default TTL for workspaces created from this template. It is
1215
the default time before shutdown - workspaces created from this

‎coderd/searchquery/search.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,8 @@ func Templates(ctx context.Context, db database.Store, query string) (database.G
198198

199199
parser:=httpapi.NewQueryParamParser()
200200
filter:= database.GetTemplatesWithFilterParams{
201-
Deleted:parser.Boolean(values,false,"deleted"),
202-
// TODO: Should name be a fuzzy search?
203-
ExactName:parser.String(values,"","name"),
201+
Deleted:parser.Boolean(values,false,"deleted"),
202+
ExactName:parser.String(values,"","exact_name"),
204203
IDs:parser.UUIDs(values, []uuid.UUID{},"ids"),
205204
Deprecated:parser.NullableBoolean(values, sql.NullBool{},"deprecated"),
206205
}

‎codersdk/organizations.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ func (c *Client) TemplatesByOrganization(ctx context.Context, organizationID uui
365365

366366
typeTemplateFilterstruct {
367367
OrganizationID uuid.UUID
368+
ExactNamestring
368369
}
369370

370371
// asRequestOption returns a function that can be used in (*Client).Request.
@@ -378,6 +379,10 @@ func (f TemplateFilter) asRequestOption() RequestOption {
378379
params=append(params,fmt.Sprintf("organization:%q",f.OrganizationID.String()))
379380
}
380381

382+
iff.ExactName!="" {
383+
params=append(params,fmt.Sprintf("exact_name:%q",f.ExactName))
384+
}
385+
381386
q:=r.URL.Query()
382387
q.Set("q",strings.Join(params," "))
383388
r.URL.RawQuery=q.Encode()

‎docs/cli/templates_create.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp