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

Commit61fac2d

Browse files
authored
feat(cli): create workspace using parameters from existing workspace (#10604)
1 parent076db31 commit61fac2d

File tree

5 files changed

+213
-8
lines changed

5 files changed

+213
-8
lines changed

‎cli/create.go

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ func (r *RootCmd) create() *clibase.Cmd {
2626
stopAfter time.Duration
2727
workspaceNamestring
2828

29-
parameterFlagsworkspaceParameterFlags
30-
autoUpdatesstring
29+
parameterFlagsworkspaceParameterFlags
30+
autoUpdatesstring
31+
copyParametersFromstring
3132
)
3233
client:=new(codersdk.Client)
3334
cmd:=&clibase.Cmd{
@@ -76,7 +77,24 @@ func (r *RootCmd) create() *clibase.Cmd {
7677
returnxerrors.Errorf("A workspace already exists named %q!",workspaceName)
7778
}
7879

80+
varsourceWorkspace codersdk.Workspace
81+
ifcopyParametersFrom!="" {
82+
sourceWorkspaceOwner,sourceWorkspaceName,err:=splitNamedWorkspace(copyParametersFrom)
83+
iferr!=nil {
84+
returnerr
85+
}
86+
87+
sourceWorkspace,err=client.WorkspaceByOwnerAndName(inv.Context(),sourceWorkspaceOwner,sourceWorkspaceName, codersdk.WorkspaceOptions{})
88+
iferr!=nil {
89+
returnxerrors.Errorf("get source workspace: %w",err)
90+
}
91+
92+
_,_=fmt.Fprintf(inv.Stdout,"Coder will use the same template %q as the source workspace.\n",sourceWorkspace.TemplateName)
93+
templateName=sourceWorkspace.TemplateName
94+
}
95+
7996
vartemplate codersdk.Template
97+
vartemplateVersionID uuid.UUID
8098
iftemplateName=="" {
8199
_,_=fmt.Fprintln(inv.Stdout,pretty.Sprint(cliui.DefaultStyles.Wrap,"Select a template below to preview the provisioned infrastructure:"))
82100

@@ -118,11 +136,19 @@ func (r *RootCmd) create() *clibase.Cmd {
118136
}
119137

120138
template=templateByName[option]
139+
templateVersionID=template.ActiveVersionID
140+
}elseifsourceWorkspace.LatestBuild.TemplateVersionID!=uuid.Nil {
141+
template,err=client.Template(inv.Context(),sourceWorkspace.TemplateID)
142+
iferr!=nil {
143+
returnxerrors.Errorf("get template by name: %w",err)
144+
}
145+
templateVersionID=sourceWorkspace.LatestBuild.TemplateVersionID
121146
}else {
122147
template,err=client.TemplateByName(inv.Context(),organization.ID,templateName)
123148
iferr!=nil {
124149
returnxerrors.Errorf("get template by name: %w",err)
125150
}
151+
templateVersionID=template.ActiveVersionID
126152
}
127153

128154
varschedSpec*string
@@ -134,18 +160,28 @@ func (r *RootCmd) create() *clibase.Cmd {
134160
schedSpec=ptr.Ref(sched.String())
135161
}
136162

137-
cliRichParameters,err:=asWorkspaceBuildParameters(parameterFlags.richParameters)
163+
cliBuildParameters,err:=asWorkspaceBuildParameters(parameterFlags.richParameters)
138164
iferr!=nil {
139165
returnxerrors.Errorf("can't parse given parameter values: %w",err)
140166
}
141167

168+
varsourceWorkspaceParameters []codersdk.WorkspaceBuildParameter
169+
ifcopyParametersFrom!="" {
170+
sourceWorkspaceParameters,err=client.WorkspaceBuildParameters(inv.Context(),sourceWorkspace.LatestBuild.ID)
171+
iferr!=nil {
172+
returnxerrors.Errorf("get source workspace build parameters: %w",err)
173+
}
174+
}
175+
142176
richParameters,err:=prepWorkspaceBuild(inv,client,prepWorkspaceBuildArgs{
143177
Action:WorkspaceCreate,
144-
TemplateVersionID:template.ActiveVersionID,
178+
TemplateVersionID:templateVersionID,
145179
NewWorkspaceName:workspaceName,
146180

147181
RichParameterFile:parameterFlags.richParameterFile,
148-
RichParameters:cliRichParameters,
182+
RichParameters:cliBuildParameters,
183+
184+
SourceWorkspaceParameters:sourceWorkspaceParameters,
149185
})
150186
iferr!=nil {
151187
returnxerrors.Errorf("prepare build: %w",err)
@@ -165,7 +201,7 @@ func (r *RootCmd) create() *clibase.Cmd {
165201
}
166202

167203
workspace,err:=client.CreateWorkspace(inv.Context(),organization.ID,workspaceOwner, codersdk.CreateWorkspaceRequest{
168-
TemplateID:template.ID,
204+
TemplateVersionID:templateVersionID,
169205
Name:workspaceName,
170206
AutostartSchedule:schedSpec,
171207
TTLMillis:ttlMillis,
@@ -217,6 +253,12 @@ func (r *RootCmd) create() *clibase.Cmd {
217253
Default:string(codersdk.AutomaticUpdatesNever),
218254
Value:clibase.StringOf(&autoUpdates),
219255
},
256+
clibase.Option{
257+
Flag:"copy-parameters-from",
258+
Env:"CODER_WORKSPACE_COPY_PARAMETERS_FROM",
259+
Description:"Specify the source workspace name to copy parameters from.",
260+
Value:clibase.StringOf(&copyParametersFrom),
261+
},
220262
cliui.SkipPromptOption(),
221263
)
222264
cmd.Options=append(cmd.Options,parameterFlags.cliParameters()...)
@@ -228,7 +270,8 @@ type prepWorkspaceBuildArgs struct {
228270
TemplateVersionID uuid.UUID
229271
NewWorkspaceNamestring
230272

231-
LastBuildParameters []codersdk.WorkspaceBuildParameter
273+
LastBuildParameters []codersdk.WorkspaceBuildParameter
274+
SourceWorkspaceParameters []codersdk.WorkspaceBuildParameter
232275

233276
PromptBuildOptionsbool
234277
BuildOptions []codersdk.WorkspaceBuildParameter
@@ -263,6 +306,7 @@ func prepWorkspaceBuild(inv *clibase.Invocation, client *codersdk.Client, args p
263306

264307
resolver:=new(ParameterResolver).
265308
WithLastBuildParameters(args.LastBuildParameters).
309+
WithSourceWorkspaceParameters(args.SourceWorkspaceParameters).
266310
WithPromptBuildOptions(args.PromptBuildOptions).
267311
WithBuildOptions(args.BuildOptions).
268312
WithPromptRichParameters(args.PromptRichParameters).

‎cli/create_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,124 @@ func TestCreateWithRichParameters(t *testing.T) {
416416
assert.ErrorContains(t,err,"parameter\""+wrongFirstParameterName+"\" is not present in the template")
417417
assert.ErrorContains(t,err,"Did you mean: "+firstParameterName)
418418
})
419+
420+
t.Run("CopyParameters",func(t*testing.T) {
421+
t.Parallel()
422+
423+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
424+
owner:=coderdtest.CreateFirstUser(t,client)
425+
member,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
426+
version:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,echoResponses)
427+
coderdtest.AwaitTemplateVersionJobCompleted(t,client,version.ID)
428+
429+
template:=coderdtest.CreateTemplate(t,client,owner.OrganizationID,version.ID)
430+
431+
// Firstly, create a regular workspace using template with parameters.
432+
inv,root:=clitest.New(t,"create","my-workspace","--template",template.Name,"-y",
433+
"--parameter",fmt.Sprintf("%s=%s",firstParameterName,firstParameterValue),
434+
"--parameter",fmt.Sprintf("%s=%s",secondParameterName,secondParameterValue),
435+
"--parameter",fmt.Sprintf("%s=%s",immutableParameterName,immutableParameterValue))
436+
clitest.SetupConfig(t,member,root)
437+
pty:=ptytest.New(t).Attach(inv)
438+
inv.Stdout=pty.Output()
439+
inv.Stderr=pty.Output()
440+
err:=inv.Run()
441+
require.NoError(t,err,"can't create first workspace")
442+
443+
// Secondly, create a new workspace using parameters from the previous workspace.
444+
constotherWorkspace="other-workspace"
445+
446+
inv,root=clitest.New(t,"create","--copy-parameters-from","my-workspace",otherWorkspace,"-y")
447+
clitest.SetupConfig(t,member,root)
448+
pty=ptytest.New(t).Attach(inv)
449+
inv.Stdout=pty.Output()
450+
inv.Stderr=pty.Output()
451+
err=inv.Run()
452+
require.NoError(t,err,"can't create a workspace based on the source workspace")
453+
454+
// Verify if the new workspace uses expected parameters.
455+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitShort)
456+
defercancel()
457+
458+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
459+
Name:otherWorkspace,
460+
})
461+
require.NoError(t,err,"can't list available workspaces")
462+
require.Len(t,workspaces.Workspaces,1)
463+
464+
otherWorkspaceLatestBuild:=workspaces.Workspaces[0].LatestBuild
465+
466+
buildParameters,err:=client.WorkspaceBuildParameters(ctx,otherWorkspaceLatestBuild.ID)
467+
require.NoError(t,err)
468+
require.Len(t,buildParameters,3)
469+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:firstParameterName,Value:firstParameterValue})
470+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:secondParameterName,Value:secondParameterValue})
471+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:immutableParameterName,Value:immutableParameterValue})
472+
})
473+
474+
t.Run("CopyParametersFromNotUpdatedWorkspace",func(t*testing.T) {
475+
t.Parallel()
476+
477+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
478+
owner:=coderdtest.CreateFirstUser(t,client)
479+
member,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
480+
version:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,echoResponses)
481+
coderdtest.AwaitTemplateVersionJobCompleted(t,client,version.ID)
482+
483+
template:=coderdtest.CreateTemplate(t,client,owner.OrganizationID,version.ID)
484+
485+
// Firstly, create a regular workspace using template with parameters.
486+
inv,root:=clitest.New(t,"create","my-workspace","--template",template.Name,"-y",
487+
"--parameter",fmt.Sprintf("%s=%s",firstParameterName,firstParameterValue),
488+
"--parameter",fmt.Sprintf("%s=%s",secondParameterName,secondParameterValue),
489+
"--parameter",fmt.Sprintf("%s=%s",immutableParameterName,immutableParameterValue))
490+
clitest.SetupConfig(t,member,root)
491+
pty:=ptytest.New(t).Attach(inv)
492+
inv.Stdout=pty.Output()
493+
inv.Stderr=pty.Output()
494+
err:=inv.Run()
495+
require.NoError(t,err,"can't create first workspace")
496+
497+
// Secondly, update the template to the newer version.
498+
version2:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,prepareEchoResponses([]*proto.RichParameter{
499+
{Name:"third_parameter",Type:"string",DefaultValue:"not-relevant"},
500+
}),func(ctvr*codersdk.CreateTemplateVersionRequest) {
501+
ctvr.TemplateID=template.ID
502+
})
503+
coderdtest.AwaitTemplateVersionJobCompleted(t,client,version2.ID)
504+
coderdtest.UpdateActiveTemplateVersion(t,client,template.ID,version2.ID)
505+
506+
// Thirdly, create a new workspace using parameters from the previous workspace.
507+
constotherWorkspace="other-workspace"
508+
509+
inv,root=clitest.New(t,"create","--copy-parameters-from","my-workspace",otherWorkspace,"-y")
510+
clitest.SetupConfig(t,member,root)
511+
pty=ptytest.New(t).Attach(inv)
512+
inv.Stdout=pty.Output()
513+
inv.Stderr=pty.Output()
514+
err=inv.Run()
515+
require.NoError(t,err,"can't create a workspace based on the source workspace")
516+
517+
// Verify if the new workspace uses expected parameters.
518+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitShort)
519+
defercancel()
520+
521+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
522+
Name:otherWorkspace,
523+
})
524+
require.NoError(t,err,"can't list available workspaces")
525+
require.Len(t,workspaces.Workspaces,1)
526+
527+
otherWorkspaceLatestBuild:=workspaces.Workspaces[0].LatestBuild
528+
require.Equal(t,version.ID,otherWorkspaceLatestBuild.TemplateVersionID)
529+
530+
buildParameters,err:=client.WorkspaceBuildParameters(ctx,otherWorkspaceLatestBuild.ID)
531+
require.NoError(t,err)
532+
require.Len(t,buildParameters,3)
533+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:firstParameterName,Value:firstParameterValue})
534+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:secondParameterName,Value:secondParameterValue})
535+
require.Contains(t,buildParameters, codersdk.WorkspaceBuildParameter{Name:immutableParameterName,Value:immutableParameterValue})
536+
})
419537
}
420538

421539
funcTestCreateValidateRichParameters(t*testing.T) {

‎cli/parameterresolver.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ const (
2323
)
2424

2525
typeParameterResolverstruct {
26-
lastBuildParameters []codersdk.WorkspaceBuildParameter
26+
lastBuildParameters []codersdk.WorkspaceBuildParameter
27+
sourceWorkspaceParameters []codersdk.WorkspaceBuildParameter
2728

2829
richParameters []codersdk.WorkspaceBuildParameter
2930
richParametersFilemap[string]string
@@ -38,6 +39,11 @@ func (pr *ParameterResolver) WithLastBuildParameters(params []codersdk.Workspace
3839
returnpr
3940
}
4041

42+
func (pr*ParameterResolver)WithSourceWorkspaceParameters(params []codersdk.WorkspaceBuildParameter)*ParameterResolver {
43+
pr.sourceWorkspaceParameters=params
44+
returnpr
45+
}
46+
4147
func (pr*ParameterResolver)WithRichParameters(params []codersdk.WorkspaceBuildParameter)*ParameterResolver {
4248
pr.richParameters=params
4349
returnpr
@@ -69,6 +75,7 @@ func (pr *ParameterResolver) Resolve(inv *clibase.Invocation, action WorkspaceCL
6975

7076
staged=pr.resolveWithParametersMapFile(staged)
7177
staged=pr.resolveWithCommandLineOrEnv(staged)
78+
staged=pr.resolveWithSourceBuildParameters(staged,templateVersionParameters)
7279
staged=pr.resolveWithLastBuildParameters(staged,templateVersionParameters)
7380
iferr=pr.verifyConstraints(staged,action,templateVersionParameters);err!=nil {
7481
returnnil,err
@@ -160,6 +167,30 @@ next:
160167
returnresolved
161168
}
162169

170+
func (pr*ParameterResolver)resolveWithSourceBuildParameters(resolved []codersdk.WorkspaceBuildParameter,templateVersionParameters []codersdk.TemplateVersionParameter) []codersdk.WorkspaceBuildParameter {
171+
next:
172+
for_,buildParameter:=rangepr.sourceWorkspaceParameters {
173+
tvp:=findTemplateVersionParameter(buildParameter,templateVersionParameters)
174+
iftvp==nil {
175+
continue// it looks like this parameter is not present anymore
176+
}
177+
178+
iftvp.Ephemeral {
179+
continue// ephemeral parameters should not be passed to consecutive builds
180+
}
181+
182+
fori,r:=rangeresolved {
183+
ifr.Name==buildParameter.Name {
184+
resolved[i].Value=buildParameter.Value
185+
continue next
186+
}
187+
}
188+
189+
resolved=append(resolved,buildParameter)
190+
}
191+
returnresolved
192+
}
193+
163194
func (pr*ParameterResolver)verifyConstraints(resolved []codersdk.WorkspaceBuildParameter,actionWorkspaceCLIAction,templateVersionParameters []codersdk.TemplateVersionParameter)error {
164195
for_,r:=rangeresolved {
165196
tvp:=findTemplateVersionParameter(r,templateVersionParameters)

‎cli/testdata/coder_create_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ OPTIONS:
1414
Specify automatic updates setting for the workspace (accepts 'always'
1515
or 'never').
1616

17+
--copy-parameters-from string, $CODER_WORKSPACE_COPY_PARAMETERS_FROM
18+
Specify the source workspace name to copy parameters from.
19+
1720
--parameter string-array, $CODER_RICH_PARAMETER
1821
Rich parameter value in the format "name=value".
1922

‎docs/cli/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