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

Commit931b97c

Browse files
authored
feat(cli): add CLI support for listing presets (#18910)
## Description This PR introduces a new `list presets` command to display the presetsassociated with a given template.By default, it displays the presets for the template's active version,unless a `--template-version` flag is provided.## Changes* Added a new `list presets` command under `coder templates presets` todisplay presets associated with a template.* By default, the command lists presets from the template’s activeversion.* Users can override the default behavior by providing the`--template-version` flag to target a specific version.```> coder templates versions presets list --helpUSAGE: coder templates presets list [flags] <template> List all presets of the specified template. Defaults to the active template version.OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. -c, --column [name|parameters|default|desired prebuild instances] (default: name,parameters,default,desired prebuild instances) Columns to display in table output. -o, --output table|json (default: table) Output format. --template-version string Specify a template version to list presets for. Defaults to the active version.```Related PR:#18912 - please considerboth PRs together as they’re part of the same workflowRelates to issue:#16594<!-- This is an auto-generated comment: release notes by coderabbit.ai-->## Summary by CodeRabbit* **New Features*** Added CLI commands to manage and list presets for specific templateversions, supporting tabular and JSON output.* Introduced a new CLI subcommand group for template version presets,including detailed help and documentation.* Added support for displaying and managing the desired number ofprebuild instances for presets in CLI, API, and UI.* **Documentation*** Updated and expanded CLI and API documentation to describe newcommands, options, and the desired prebuild instances field in presets.* Added new help output and reference files for template version presetscommands.* **Bug Fixes*** Ensured correct handling and display of the desired prebuild instancesproperty for presets across CLI, API, and UI.* **Tests*** Introduced end-to-end tests for listing template version presets,covering scenarios with and without presets.<!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent070178c commit931b97c

18 files changed

+584
-22
lines changed

‎cli/templatepresets.go‎

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"golang.org/x/xerrors"
9+
10+
"github.com/coder/coder/v2/cli/cliui"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/serpent"
13+
)
14+
15+
func (r*RootCmd)templatePresets()*serpent.Command {
16+
cmd:=&serpent.Command{
17+
Use:"presets",
18+
Short:"Manage presets of the specified template",
19+
Aliases: []string{"preset"},
20+
Long:FormatExamples(
21+
Example{
22+
Description:"List presets for the active version of a template",
23+
Command:"coder templates presets list my-template",
24+
},
25+
Example{
26+
Description:"List presets for a specific version of a template",
27+
Command:"coder templates presets list my-template --template-version my-template-version",
28+
},
29+
),
30+
Handler:func(inv*serpent.Invocation)error {
31+
returninv.Command.HelpHandler(inv)
32+
},
33+
Children: []*serpent.Command{
34+
r.templatePresetsList(),
35+
},
36+
}
37+
38+
returncmd
39+
}
40+
41+
func (r*RootCmd)templatePresetsList()*serpent.Command {
42+
defaultColumns:= []string{
43+
"name",
44+
"parameters",
45+
"default",
46+
"desired prebuild instances",
47+
}
48+
formatter:=cliui.NewOutputFormatter(
49+
cliui.TableFormat([]templatePresetRow{},defaultColumns),
50+
cliui.JSONFormat(),
51+
)
52+
client:=new(codersdk.Client)
53+
orgContext:=NewOrganizationContext()
54+
55+
vartemplateVersionstring
56+
57+
cmd:=&serpent.Command{
58+
Use:"list <template>",
59+
Middleware:serpent.Chain(
60+
serpent.RequireNArgs(1),
61+
r.InitClient(client),
62+
),
63+
Short:"List all presets of the specified template. Defaults to the active template version.",
64+
Options: serpent.OptionSet{
65+
{
66+
Name:"template-version",
67+
Description:"Specify a template version to list presets for. Defaults to the active version.",
68+
Flag:"template-version",
69+
Value:serpent.StringOf(&templateVersion),
70+
},
71+
},
72+
Handler:func(inv*serpent.Invocation)error {
73+
organization,err:=orgContext.Selected(inv,client)
74+
iferr!=nil {
75+
returnxerrors.Errorf("get current organization: %w",err)
76+
}
77+
78+
template,err:=client.TemplateByName(inv.Context(),organization.ID,inv.Args[0])
79+
iferr!=nil {
80+
returnxerrors.Errorf("get template by name: %w",err)
81+
}
82+
83+
// If a template version is specified via flag, fetch that version by name
84+
varversion codersdk.TemplateVersion
85+
iflen(templateVersion)>0 {
86+
version,err=client.TemplateVersionByName(inv.Context(),template.ID,templateVersion)
87+
iferr!=nil {
88+
returnxerrors.Errorf("get template version by name: %w",err)
89+
}
90+
}else {
91+
// Otherwise, use the template's active version
92+
version,err=client.TemplateVersion(inv.Context(),template.ActiveVersionID)
93+
iferr!=nil {
94+
returnxerrors.Errorf("get active template version: %w",err)
95+
}
96+
}
97+
98+
presets,err:=client.TemplateVersionPresets(inv.Context(),version.ID)
99+
iferr!=nil {
100+
returnxerrors.Errorf("get template versions presets by template version: %w",err)
101+
}
102+
103+
iflen(presets)==0 {
104+
cliui.Infof(
105+
inv.Stdout,
106+
"No presets found for template %q and template-version %q.\n",template.Name,version.Name,
107+
)
108+
returnnil
109+
}
110+
111+
cliui.Infof(
112+
inv.Stdout,
113+
"Showing presets for template %q and template version %q.\n",template.Name,version.Name,
114+
)
115+
rows:=templatePresetsToRows(presets...)
116+
out,err:=formatter.Format(inv.Context(),rows)
117+
iferr!=nil {
118+
returnxerrors.Errorf("render table: %w",err)
119+
}
120+
121+
_,err=fmt.Fprintln(inv.Stdout,out)
122+
returnerr
123+
},
124+
}
125+
126+
orgContext.AttachOptions(cmd)
127+
formatter.AttachOptions(&cmd.Options)
128+
returncmd
129+
}
130+
131+
typetemplatePresetRowstruct {
132+
// For json format:
133+
TemplatePreset codersdk.Preset`table:"-"`
134+
135+
// For table format:
136+
Namestring`json:"-" table:"name,default_sort"`
137+
Parametersstring`json:"-" table:"parameters"`
138+
Defaultbool`json:"-" table:"default"`
139+
DesiredPrebuildInstancesstring`json:"-" table:"desired prebuild instances"`
140+
}
141+
142+
funcformatPresetParameters(params []codersdk.PresetParameter)string {
143+
varparamsStr []string
144+
for_,p:=rangeparams {
145+
paramsStr=append(paramsStr,fmt.Sprintf("%s=%s",p.Name,p.Value))
146+
}
147+
returnstrings.Join(paramsStr,",")
148+
}
149+
150+
// templatePresetsToRows converts a list of presets to a list of rows
151+
// for outputting.
152+
functemplatePresetsToRows(presets...codersdk.Preset) []templatePresetRow {
153+
rows:=make([]templatePresetRow,len(presets))
154+
fori,preset:=rangepresets {
155+
prebuildInstances:="-"
156+
ifpreset.DesiredPrebuildInstances!=nil {
157+
prebuildInstances=strconv.Itoa(*preset.DesiredPrebuildInstances)
158+
}
159+
rows[i]=templatePresetRow{
160+
Name:preset.Name,
161+
Parameters:formatPresetParameters(preset.Parameters),
162+
Default:preset.Default,
163+
DesiredPrebuildInstances:prebuildInstances,
164+
}
165+
}
166+
167+
returnrows
168+
}

‎cli/templatepresets_test.go‎

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package cli_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/cli/clitest"
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/coder/v2/provisioner/echo"
13+
"github.com/coder/coder/v2/provisionersdk/proto"
14+
"github.com/coder/coder/v2/pty/ptytest"
15+
"github.com/coder/coder/v2/testutil"
16+
)
17+
18+
funcTestTemplatePresets(t*testing.T) {
19+
t.Parallel()
20+
21+
t.Run("NoPresets",func(t*testing.T) {
22+
t.Parallel()
23+
24+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
25+
owner:=coderdtest.CreateFirstUser(t,client)
26+
member,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
27+
28+
// Given: a template version without presets
29+
version:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,templateWithPresets([]*proto.Preset{}))
30+
_=coderdtest.AwaitTemplateVersionJobCompleted(t,client,version.ID)
31+
template:=coderdtest.CreateTemplate(t,client,owner.OrganizationID,version.ID)
32+
33+
// When: listing presets for that template
34+
inv,root:=clitest.New(t,"templates","presets","list",template.Name)
35+
clitest.SetupConfig(t,member,root)
36+
37+
pty:=ptytest.New(t).Attach(inv)
38+
doneChan:=make(chanstruct{})
39+
varrunErrerror
40+
gofunc() {
41+
deferclose(doneChan)
42+
runErr=inv.Run()
43+
}()
44+
<-doneChan
45+
require.NoError(t,runErr)
46+
47+
// Should return a message when no presets are found for the given template and version.
48+
notFoundMessage:=fmt.Sprintf("No presets found for template %q and template-version %q.",template.Name,version.Name)
49+
pty.ExpectRegexMatch(notFoundMessage)
50+
})
51+
52+
t.Run("ListsPresetsForDefaultTemplateVersion",func(t*testing.T) {
53+
t.Parallel()
54+
55+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
56+
owner:=coderdtest.CreateFirstUser(t,client)
57+
member,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
58+
59+
// Given: an active template version that includes presets
60+
presets:= []*proto.Preset{
61+
{
62+
Name:"preset-multiple-params",
63+
Parameters: []*proto.PresetParameter{
64+
{
65+
Name:"k1",
66+
Value:"v1",
67+
}, {
68+
Name:"k2",
69+
Value:"v2",
70+
},
71+
},
72+
},
73+
{
74+
Name:"preset-default",
75+
Default:true,
76+
Parameters: []*proto.PresetParameter{
77+
{
78+
Name:"k1",
79+
Value:"v2",
80+
},
81+
},
82+
Prebuild:&proto.Prebuild{
83+
Instances:0,
84+
},
85+
},
86+
{
87+
Name:"preset-prebuilds",
88+
Parameters: []*proto.PresetParameter{},
89+
Prebuild:&proto.Prebuild{
90+
Instances:2,
91+
},
92+
},
93+
}
94+
version:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,templateWithPresets(presets))
95+
_=coderdtest.AwaitTemplateVersionJobCompleted(t,client,version.ID)
96+
template:=coderdtest.CreateTemplate(t,client,owner.OrganizationID,version.ID)
97+
require.Equal(t,version.ID,template.ActiveVersionID)
98+
99+
// When: listing presets for that template
100+
inv,root:=clitest.New(t,"templates","presets","list",template.Name)
101+
clitest.SetupConfig(t,member,root)
102+
103+
pty:=ptytest.New(t).Attach(inv)
104+
doneChan:=make(chanstruct{})
105+
varrunErrerror
106+
gofunc() {
107+
deferclose(doneChan)
108+
runErr=inv.Run()
109+
}()
110+
111+
<-doneChan
112+
require.NoError(t,runErr)
113+
114+
// Should: return the active version's presets sorted by name
115+
message:=fmt.Sprintf("Showing presets for template %q and template version %q.",template.Name,version.Name)
116+
pty.ExpectMatch(message)
117+
pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
118+
// The parameter order is not guaranteed in the output, so we match both possible orders
119+
pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
120+
pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
121+
})
122+
123+
t.Run("ListsPresetsForSpecifiedTemplateVersion",func(t*testing.T) {
124+
t.Parallel()
125+
126+
ctx:=testutil.Context(t,testutil.WaitMedium)
127+
128+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
129+
owner:=coderdtest.CreateFirstUser(t,client)
130+
member,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
131+
132+
// Given: a template with an active version that has no presets,
133+
// and another template version that includes presets
134+
presets:= []*proto.Preset{
135+
{
136+
Name:"preset-multiple-params",
137+
Parameters: []*proto.PresetParameter{
138+
{
139+
Name:"k1",
140+
Value:"v1",
141+
}, {
142+
Name:"k2",
143+
Value:"v2",
144+
},
145+
},
146+
},
147+
{
148+
Name:"preset-default",
149+
Default:true,
150+
Parameters: []*proto.PresetParameter{
151+
{
152+
Name:"k1",
153+
Value:"v2",
154+
},
155+
},
156+
Prebuild:&proto.Prebuild{
157+
Instances:0,
158+
},
159+
},
160+
{
161+
Name:"preset-prebuilds",
162+
Parameters: []*proto.PresetParameter{},
163+
Prebuild:&proto.Prebuild{
164+
Instances:2,
165+
},
166+
},
167+
}
168+
// Given: first template version with presets
169+
version:=coderdtest.CreateTemplateVersion(t,client,owner.OrganizationID,templateWithPresets(presets))
170+
_=coderdtest.AwaitTemplateVersionJobCompleted(t,client,version.ID)
171+
template:=coderdtest.CreateTemplate(t,client,owner.OrganizationID,version.ID)
172+
// Given: second template version without presets
173+
activeVersion:=coderdtest.UpdateTemplateVersion(t,client,owner.OrganizationID,templateWithPresets([]*proto.Preset{}),template.ID)
174+
_=coderdtest.AwaitTemplateVersionJobCompleted(t,client,activeVersion.ID)
175+
// Given: second template version is the active version
176+
err:=client.UpdateActiveTemplateVersion(ctx,template.ID, codersdk.UpdateActiveTemplateVersion{
177+
ID:activeVersion.ID,
178+
})
179+
require.NoError(t,err)
180+
updatedTemplate,err:=client.Template(ctx,template.ID)
181+
require.NoError(t,err)
182+
require.Equal(t,activeVersion.ID,updatedTemplate.ActiveVersionID)
183+
// Given: template has two versions
184+
templateVersions,err:=client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
185+
TemplateID:updatedTemplate.ID,
186+
})
187+
require.NoError(t,err)
188+
require.Len(t,templateVersions,2)
189+
190+
// When: listing presets for a specific template and its specified version
191+
inv,root:=clitest.New(t,"templates","presets","list",updatedTemplate.Name,"--template-version",version.Name)
192+
clitest.SetupConfig(t,member,root)
193+
194+
pty:=ptytest.New(t).Attach(inv)
195+
doneChan:=make(chanstruct{})
196+
varrunErrerror
197+
gofunc() {
198+
deferclose(doneChan)
199+
runErr=inv.Run()
200+
}()
201+
202+
<-doneChan
203+
require.NoError(t,runErr)
204+
205+
// Should: return the specified version's presets sorted by name
206+
message:=fmt.Sprintf("Showing presets for template %q and template version %q.",template.Name,version.Name)
207+
pty.ExpectMatch(message)
208+
pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
209+
// The parameter order is not guaranteed in the output, so we match both possible orders
210+
pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
211+
pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
212+
})
213+
}
214+
215+
functemplateWithPresets(presets []*proto.Preset)*echo.Responses {
216+
return&echo.Responses{
217+
Parse:echo.ParseComplete,
218+
ProvisionPlan: []*proto.Response{
219+
{
220+
Type:&proto.Response_Plan{
221+
Plan:&proto.PlanComplete{
222+
Presets:presets,
223+
},
224+
},
225+
},
226+
},
227+
}
228+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp