Skip to content

Commit a69137b

Browse files
authored
feat: Update CLI to handle managed variables (#6220)
* WIP * hcl * useManagedVariables * fix * Fix * Fix * fix * go:build * Fix * fix: bool flag * Insert template variables * API * fix * Expose via API * More wiring * CLI for testing purposes * WIP * Delete FIXME * planVars * WIP * WIP * UserVariableValues * no dry run * Dry run * Done FIXME * Fix * Fix: CLI * Fix: migration * API tests * Test info * Tests * More tests * fix: lint * Fix: authz * Address PR comments * Fix * fix * fix * CLI: create * unit tests: create templates with variables * Use last variables * Fix * Fix * Fix * Push tests * fix: variable is required if Default is nil * WIP * Redact sensitive values * Fixes * Fixes * Fix: arg description * Fix * Variable param * Fix: gen * Fix * Fix: goldens
1 parent d5af536 commit a69137b

15 files changed

+626
-24
lines changed

cli/templatecreate.go

+17-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ func templateCreate() *cobra.Command {
2525
provisioner string
2626
provisionerTags []string
2727
parameterFile string
28+
variablesFile string
29+
variables []string
2830
defaultTTL time.Duration
2931

3032
uploadFlags templateUploadFlags
@@ -76,6 +78,8 @@ func templateCreate() *cobra.Command {
7678
FileID: resp.ID,
7779
ParameterFile: parameterFile,
7880
ProvisionerTags: tags,
81+
VariablesFile: variablesFile,
82+
Variables: variables,
7983
})
8084
if err != nil {
8185
return err
@@ -113,6 +117,8 @@ func templateCreate() *cobra.Command {
113117
},
114118
}
115119
cmd.Flags().StringVarP(&parameterFile, "parameter-file", "", "", "Specify a file path with parameter values.")
120+
cmd.Flags().StringVarP(&variablesFile, "variables-file", "", "", "Specify a file path with values for Terraform-managed variables.")
121+
cmd.Flags().StringArrayVarP(&variables, "variable", "", []string{}, "Specify a set of values for Terraform-managed variables.")
116122
cmd.Flags().StringArrayVarP(&provisionerTags, "provisioner-tag", "", []string{}, "Specify a set of tags to target provisioner daemons.")
117123
cmd.Flags().DurationVarP(&defaultTTL, "default-ttl", "", 24*time.Hour, "Specify a default TTL for workspaces created from this template.")
118124
uploadFlags.register(cmd.Flags())
@@ -133,7 +139,10 @@ type createValidTemplateVersionArgs struct {
133139
Provisioner database.ProvisionerType
134140
FileID uuid.UUID
135141
ParameterFile string
136-
ValuesFile string
142+
143+
VariablesFile string
144+
Variables []string
145+
137146
// Template is only required if updating a template's active version.
138147
Template *codersdk.Template
139148
// ReuseParameters will attempt to reuse params from the Template field
@@ -146,12 +155,16 @@ type createValidTemplateVersionArgs struct {
146155
func createValidTemplateVersion(cmd *cobra.Command, args createValidTemplateVersionArgs, parameters ...codersdk.CreateParameterRequest) (*codersdk.TemplateVersion, []codersdk.CreateParameterRequest, error) {
147156
client := args.Client
148157

149-
// FIXME(mtojek): I will iterate on CLI experience in the follow-up.
150-
// see: https://github.com/coder/coder/issues/5980
151-
variableValues, err := loadVariableValues(args.ValuesFile)
158+
variableValues, err := loadVariableValuesFromFile(args.VariablesFile)
159+
if err != nil {
160+
return nil, nil, err
161+
}
162+
163+
variableValuesFromKeyValues, err := loadVariableValuesFromOptions(args.Variables)
152164
if err != nil {
153165
return nil, nil, err
154166
}
167+
variableValues = append(variableValues, variableValuesFromKeyValues...)
155168

156169
req := codersdk.CreateTemplateVersionRequest{
157170
Name: args.Name,

cli/templatecreate_test.go

+155
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,161 @@ func TestTemplateCreate(t *testing.T) {
299299

300300
require.EqualError(t, <-execDone, "Template name must be less than 32 characters")
301301
})
302+
303+
t.Run("WithVariablesFileWithoutRequiredValue", func(t *testing.T) {
304+
t.Parallel()
305+
306+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
307+
coderdtest.CreateFirstUser(t, client)
308+
309+
templateVariables := []*proto.TemplateVariable{
310+
{
311+
Name: "first_variable",
312+
Description: "This is the first variable",
313+
Type: "string",
314+
Required: true,
315+
Sensitive: true,
316+
},
317+
{
318+
Name: "second_variable",
319+
Description: "This is the first variable",
320+
Type: "string",
321+
DefaultValue: "abc",
322+
Required: false,
323+
Sensitive: true,
324+
},
325+
}
326+
source := clitest.CreateTemplateVersionSource(t,
327+
createEchoResponsesWithTemplateVariables(templateVariables))
328+
tempDir := t.TempDir()
329+
removeTmpDirUntilSuccessAfterTest(t, tempDir)
330+
variablesFile, _ := os.CreateTemp(tempDir, "variables*.yaml")
331+
_, _ = variablesFile.WriteString(`second_variable: foobar`)
332+
cmd, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--variables-file", variablesFile.Name())
333+
clitest.SetupConfig(t, client, root)
334+
pty := ptytest.New(t)
335+
cmd.SetIn(pty.Input())
336+
cmd.SetOut(pty.Output())
337+
338+
execDone := make(chan error)
339+
go func() {
340+
execDone <- cmd.Execute()
341+
}()
342+
343+
matches := []struct {
344+
match string
345+
write string
346+
}{
347+
{match: "Upload", write: "yes"},
348+
}
349+
for _, m := range matches {
350+
pty.ExpectMatch(m.match)
351+
if len(m.write) > 0 {
352+
pty.WriteLine(m.write)
353+
}
354+
}
355+
356+
require.Error(t, <-execDone)
357+
})
358+
359+
t.Run("WithVariablesFileWithTheRequiredValue", func(t *testing.T) {
360+
t.Parallel()
361+
362+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
363+
coderdtest.CreateFirstUser(t, client)
364+
365+
templateVariables := []*proto.TemplateVariable{
366+
{
367+
Name: "first_variable",
368+
Description: "This is the first variable",
369+
Type: "string",
370+
Required: true,
371+
Sensitive: true,
372+
},
373+
{
374+
Name: "second_variable",
375+
Description: "This is the second variable",
376+
Type: "string",
377+
DefaultValue: "abc",
378+
Required: false,
379+
Sensitive: true,
380+
},
381+
}
382+
source := clitest.CreateTemplateVersionSource(t,
383+
createEchoResponsesWithTemplateVariables(templateVariables))
384+
tempDir := t.TempDir()
385+
removeTmpDirUntilSuccessAfterTest(t, tempDir)
386+
variablesFile, _ := os.CreateTemp(tempDir, "variables*.yaml")
387+
_, _ = variablesFile.WriteString(`first_variable: foobar`)
388+
cmd, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--variables-file", variablesFile.Name())
389+
clitest.SetupConfig(t, client, root)
390+
pty := ptytest.New(t)
391+
cmd.SetIn(pty.Input())
392+
cmd.SetOut(pty.Output())
393+
394+
execDone := make(chan error)
395+
go func() {
396+
execDone <- cmd.Execute()
397+
}()
398+
399+
matches := []struct {
400+
match string
401+
write string
402+
}{
403+
{match: "Upload", write: "yes"},
404+
{match: "Confirm create?", write: "yes"},
405+
}
406+
for _, m := range matches {
407+
pty.ExpectMatch(m.match)
408+
if len(m.write) > 0 {
409+
pty.WriteLine(m.write)
410+
}
411+
}
412+
413+
require.NoError(t, <-execDone)
414+
})
415+
t.Run("WithVariableOption", func(t *testing.T) {
416+
t.Parallel()
417+
418+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
419+
coderdtest.CreateFirstUser(t, client)
420+
421+
templateVariables := []*proto.TemplateVariable{
422+
{
423+
Name: "first_variable",
424+
Description: "This is the first variable",
425+
Type: "string",
426+
Required: true,
427+
Sensitive: true,
428+
},
429+
}
430+
source := clitest.CreateTemplateVersionSource(t,
431+
createEchoResponsesWithTemplateVariables(templateVariables))
432+
cmd, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--variable", "first_variable=foobar")
433+
clitest.SetupConfig(t, client, root)
434+
pty := ptytest.New(t)
435+
cmd.SetIn(pty.Input())
436+
cmd.SetOut(pty.Output())
437+
438+
execDone := make(chan error)
439+
go func() {
440+
execDone <- cmd.Execute()
441+
}()
442+
443+
matches := []struct {
444+
match string
445+
write string
446+
}{
447+
{match: "Upload", write: "yes"},
448+
{match: "Confirm create?", write: "yes"},
449+
}
450+
for _, m := range matches {
451+
pty.ExpectMatch(m.match)
452+
pty.WriteLine(m.write)
453+
}
454+
455+
require.NoError(t, <-execDone)
456+
})
302457
}
303458

304459
func createTestParseResponse() []*proto.Parse_Response {

cli/templatepush.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ func templatePush() *cobra.Command {
9393
versionName string
9494
provisioner string
9595
parameterFile string
96-
valuesFile string
96+
variablesFile string
97+
variables []string
9798
alwaysPrompt bool
9899
provisionerTags []string
99100
uploadFlags templateUploadFlags
@@ -140,7 +141,8 @@ func templatePush() *cobra.Command {
140141
Provisioner: database.ProvisionerType(provisioner),
141142
FileID: resp.ID,
142143
ParameterFile: parameterFile,
143-
ValuesFile: valuesFile,
144+
VariablesFile: variablesFile,
145+
Variables: variables,
144146
Template: &template,
145147
ReuseParameters: !alwaysPrompt,
146148
ProvisionerTags: tags,
@@ -167,7 +169,8 @@ func templatePush() *cobra.Command {
167169

168170
cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend")
169171
cmd.Flags().StringVarP(&parameterFile, "parameter-file", "", "", "Specify a file path with parameter values.")
170-
cmd.Flags().StringVarP(&valuesFile, "values-file", "", "", "Specify a file path with values for managed variables.")
172+
cmd.Flags().StringVarP(&variablesFile, "variables-file", "", "", "Specify a file path with values for Terraform-managed variables.")
173+
cmd.Flags().StringArrayVarP(&variables, "variable", "", []string{}, "Specify a set of values for Terraform-managed variables.")
171174
cmd.Flags().StringVarP(&versionName, "name", "", "", "Specify a name for the new template version. It will be automatically generated if not provided.")
172175
cmd.Flags().StringArrayVarP(&provisionerTags, "provisioner-tag", "", []string{}, "Specify a set of tags to target provisioner daemons.")
173176
cmd.Flags().BoolVar(&alwaysPrompt, "always-prompt", false, "Always prompt all parameters. Does not pull parameter values from active template version")

0 commit comments

Comments
 (0)