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

Commit8a1db7b

Browse files
AbhineetJainkylecarbs
authored andcommitted
feat: Read params from file for template/workspace creation (#1541)
* Read params from file for template/workspace creation* Use os.ReadFile* Refactor reading params into a separate module* Add comments and unit tests* Rename variable* Uncomment and fix unit test* Fix comment* Refactor tests* Fix unit tests for windows* Fix unit tests for Windows* Add comments for the hotfix
1 parenta0e4212 commit8a1db7b

File tree

7 files changed

+431
-47
lines changed

7 files changed

+431
-47
lines changed

‎cli/create.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func create() *cobra.Command {
1717
var (
1818
workspaceNamestring
1919
templateNamestring
20+
parameterFilestring
2021
)
2122
cmd:=&cobra.Command{
2223
Annotations:workspaceCommand,
@@ -116,23 +117,33 @@ func create() *cobra.Command {
116117
returnerr
117118
}
118119

119-
printed:=false
120+
// parameterMapFromFile can be nil if parameter file is not specified
121+
varparameterMapFromFilemap[string]string
122+
ifparameterFile!="" {
123+
_,_=fmt.Fprintln(cmd.OutOrStdout(),cliui.Styles.Paragraph.Render("Attempting to read the variables from the parameter file.")+"\r\n")
124+
parameterMapFromFile,err=createParameterMapFromFile(parameterFile)
125+
iferr!=nil {
126+
returnerr
127+
}
128+
}
129+
130+
disclaimerPrinted:=false
120131
parameters:=make([]codersdk.CreateParameterRequest,0)
121132
for_,parameterSchema:=rangeparameterSchemas {
122133
if!parameterSchema.AllowOverrideSource {
123134
continue
124135
}
125-
if!printed {
136+
if!disclaimerPrinted {
126137
_,_=fmt.Fprintln(cmd.OutOrStdout(),cliui.Styles.Paragraph.Render("This template has customizable parameters. Values can be changed after create, but may have unintended side effects (like data loss).")+"\r\n")
127-
printed=true
138+
disclaimerPrinted=true
128139
}
129-
value,err:=cliui.ParameterSchema(cmd,parameterSchema)
140+
parameterValue,err:=getParameterValueFromMapOrInput(cmd,parameterMapFromFile,parameterSchema)
130141
iferr!=nil {
131142
returnerr
132143
}
133144
parameters=append(parameters, codersdk.CreateParameterRequest{
134145
Name:parameterSchema.Name,
135-
SourceValue:value,
146+
SourceValue:parameterValue,
136147
SourceScheme:codersdk.ParameterSourceSchemeData,
137148
DestinationScheme:parameterSchema.DefaultDestinationScheme,
138149
})
@@ -194,5 +205,6 @@ func create() *cobra.Command {
194205
}
195206

196207
cliflag.StringVarP(cmd.Flags(),&templateName,"template","t","CODER_TEMPLATE_NAME","","Specify a template name.")
208+
cliflag.StringVarP(cmd.Flags(),&parameterFile,"parameter-file","","CODER_PARAMETER_FILE","","Specify a file path with parameter values.")
197209
returncmd
198210
}

‎cli/create_test.go

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli_test
22

33
import (
44
"fmt"
5+
"os"
56
"testing"
67

78
"github.com/stretchr/testify/require"
@@ -113,39 +114,7 @@ func TestCreate(t *testing.T) {
113114

114115
defaultValue:="something"
115116
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
116-
Parse: []*proto.Parse_Response{{
117-
Type:&proto.Parse_Response_Complete{
118-
Complete:&proto.Parse_Complete{
119-
ParameterSchemas: []*proto.ParameterSchema{
120-
{
121-
AllowOverrideSource:true,
122-
Name:"region",
123-
Description:"description 1",
124-
DefaultSource:&proto.ParameterSource{
125-
Scheme:proto.ParameterSource_DATA,
126-
Value:defaultValue,
127-
},
128-
DefaultDestination:&proto.ParameterDestination{
129-
Scheme:proto.ParameterDestination_PROVISIONER_VARIABLE,
130-
},
131-
},
132-
{
133-
AllowOverrideSource:true,
134-
Name:"username",
135-
Description:"description 2",
136-
DefaultSource:&proto.ParameterSource{
137-
Scheme:proto.ParameterSource_DATA,
138-
// No default value
139-
Value:"",
140-
},
141-
DefaultDestination:&proto.ParameterDestination{
142-
Scheme:proto.ParameterDestination_PROVISIONER_VARIABLE,
143-
},
144-
},
145-
},
146-
},
147-
},
148-
}},
117+
Parse:createTestParseResponseWithDefault(defaultValue),
149118
Provision:echo.ProvisionComplete,
150119
ProvisionDryRun:echo.ProvisionComplete,
151120
})
@@ -178,4 +147,113 @@ func TestCreate(t *testing.T) {
178147
}
179148
<-doneChan
180149
})
150+
151+
t.Run("WithParameterFileContainingTheValue",func(t*testing.T) {
152+
t.Parallel()
153+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerD:true})
154+
user:=coderdtest.CreateFirstUser(t,client)
155+
156+
defaultValue:="something"
157+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
158+
Parse:createTestParseResponseWithDefault(defaultValue),
159+
Provision:echo.ProvisionComplete,
160+
ProvisionDryRun:echo.ProvisionComplete,
161+
})
162+
163+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
164+
_=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
165+
tempDir:=t.TempDir()
166+
parameterFile,_:=os.CreateTemp(tempDir,"testParameterFile*.yaml")
167+
_,_=parameterFile.WriteString("region:\"bingo\"\nusername:\"boingo\"")
168+
cmd,root:=clitest.New(t,"create","","--parameter-file",parameterFile.Name())
169+
clitest.SetupConfig(t,client,root)
170+
doneChan:=make(chanstruct{})
171+
pty:=ptytest.New(t)
172+
cmd.SetIn(pty.Input())
173+
cmd.SetOut(pty.Output())
174+
gofunc() {
175+
deferclose(doneChan)
176+
err:=cmd.Execute()
177+
require.NoError(t,err)
178+
}()
179+
180+
matches:= []string{
181+
"Specify a name","my-workspace",
182+
"Confirm create?","yes",
183+
}
184+
fori:=0;i<len(matches);i+=2 {
185+
match:=matches[i]
186+
value:=matches[i+1]
187+
pty.ExpectMatch(match)
188+
pty.WriteLine(value)
189+
}
190+
<-doneChan
191+
removeTmpDirUntilSuccess(t,tempDir)
192+
})
193+
t.Run("WithParameterFileNotContainingTheValue",func(t*testing.T) {
194+
t.Parallel()
195+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerD:true})
196+
user:=coderdtest.CreateFirstUser(t,client)
197+
198+
defaultValue:="something"
199+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
200+
Parse:createTestParseResponseWithDefault(defaultValue),
201+
Provision:echo.ProvisionComplete,
202+
ProvisionDryRun:echo.ProvisionComplete,
203+
})
204+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
205+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
206+
tempDir:=t.TempDir()
207+
parameterFile,_:=os.CreateTemp(tempDir,"testParameterFile*.yaml")
208+
_,_=parameterFile.WriteString("zone:\"bananas\"")
209+
cmd,root:=clitest.New(t,"create","my-workspace","--template",template.Name,"--parameter-file",parameterFile.Name())
210+
clitest.SetupConfig(t,client,root)
211+
doneChan:=make(chanstruct{})
212+
pty:=ptytest.New(t)
213+
cmd.SetIn(pty.Input())
214+
cmd.SetOut(pty.Output())
215+
gofunc() {
216+
deferclose(doneChan)
217+
err:=cmd.Execute()
218+
require.EqualError(t,err,"Parameter value absent in parameter file for\"region\"!")
219+
}()
220+
<-doneChan
221+
removeTmpDirUntilSuccess(t,tempDir)
222+
})
223+
}
224+
225+
funccreateTestParseResponseWithDefault(defaultValuestring) []*proto.Parse_Response {
226+
return []*proto.Parse_Response{{
227+
Type:&proto.Parse_Response_Complete{
228+
Complete:&proto.Parse_Complete{
229+
ParameterSchemas: []*proto.ParameterSchema{
230+
{
231+
AllowOverrideSource:true,
232+
Name:"region",
233+
Description:"description 1",
234+
DefaultSource:&proto.ParameterSource{
235+
Scheme:proto.ParameterSource_DATA,
236+
Value:defaultValue,
237+
},
238+
DefaultDestination:&proto.ParameterDestination{
239+
Scheme:proto.ParameterDestination_PROVISIONER_VARIABLE,
240+
},
241+
},
242+
{
243+
AllowOverrideSource:true,
244+
Name:"username",
245+
Description:"description 2",
246+
DefaultSource:&proto.ParameterSource{
247+
Scheme:proto.ParameterSource_DATA,
248+
// No default value
249+
Value:"",
250+
},
251+
DefaultDestination:&proto.ParameterDestination{
252+
Scheme:proto.ParameterDestination_PROVISIONER_VARIABLE,
253+
},
254+
},
255+
},
256+
},
257+
},
258+
}}
181259
}

‎cli/parameter.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package cli
2+
3+
import (
4+
"os"
5+
6+
"golang.org/x/xerrors"
7+
"gopkg.in/yaml.v3"
8+
9+
"github.com/coder/coder/cli/cliui"
10+
"github.com/coder/coder/codersdk"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
// Reads a YAML file and populates a string -> string map.
15+
// Throws an error if the file name is empty.
16+
funccreateParameterMapFromFile(parameterFilestring) (map[string]string,error) {
17+
ifparameterFile!="" {
18+
parameterMap:=make(map[string]string)
19+
20+
parameterFileContents,err:=os.ReadFile(parameterFile)
21+
22+
iferr!=nil {
23+
returnnil,err
24+
}
25+
26+
err=yaml.Unmarshal(parameterFileContents,&parameterMap)
27+
28+
iferr!=nil {
29+
returnnil,err
30+
}
31+
32+
returnparameterMap,nil
33+
}
34+
35+
returnnil,xerrors.Errorf("Parameter file name is not specified")
36+
}
37+
38+
// Returns a parameter value from a given map, if the map exists, else takes input from the user.
39+
// Throws an error if the map exists but does not include a value for the parameter.
40+
funcgetParameterValueFromMapOrInput(cmd*cobra.Command,parameterMapmap[string]string,parameterSchema codersdk.ParameterSchema) (string,error) {
41+
varparameterValuestring
42+
ifparameterMap!=nil {
43+
varokbool
44+
parameterValue,ok=parameterMap[parameterSchema.Name]
45+
if!ok {
46+
return"",xerrors.Errorf("Parameter value absent in parameter file for %q!",parameterSchema.Name)
47+
}
48+
}else {
49+
varerrerror
50+
parameterValue,err=cliui.ParameterSchema(cmd,parameterSchema)
51+
iferr!=nil {
52+
return"",err
53+
}
54+
}
55+
returnparameterValue,nil
56+
}

‎cli/parameter_internal_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cli
2+
3+
import (
4+
"os"
5+
"runtime"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
funcTestCreateParameterMapFromFile(t*testing.T) {
12+
t.Parallel()
13+
t.Run("CreateParameterMapFromFile",func(t*testing.T) {
14+
t.Parallel()
15+
tempDir:=t.TempDir()
16+
parameterFile,_:=os.CreateTemp(tempDir,"testParameterFile*.yaml")
17+
_,_=parameterFile.WriteString("region:\"bananas\"\ndisk:\"20\"\n")
18+
19+
parameterMapFromFile,err:=createParameterMapFromFile(parameterFile.Name())
20+
21+
expectedMap:=map[string]string{
22+
"region":"bananas",
23+
"disk":"20",
24+
}
25+
26+
assert.Equal(t,expectedMap,parameterMapFromFile)
27+
assert.Nil(t,err)
28+
29+
removeTmpDirUntilSuccess(t,tempDir)
30+
})
31+
t.Run("WithEmptyFilename",func(t*testing.T) {
32+
t.Parallel()
33+
34+
parameterMapFromFile,err:=createParameterMapFromFile("")
35+
36+
assert.Nil(t,parameterMapFromFile)
37+
assert.EqualError(t,err,"Parameter file name is not specified")
38+
})
39+
t.Run("WithInvalidFilename",func(t*testing.T) {
40+
t.Parallel()
41+
42+
parameterMapFromFile,err:=createParameterMapFromFile("invalidFile.yaml")
43+
44+
assert.Nil(t,parameterMapFromFile)
45+
46+
// On Unix based systems, it is: `open invalidFile.yaml: no such file or directory`
47+
// On Windows, it is `open invalidFile.yaml: The system cannot find the file specified.`
48+
ifruntime.GOOS=="windows" {
49+
assert.EqualError(t,err,"open invalidFile.yaml: The system cannot find the file specified.")
50+
}else {
51+
assert.EqualError(t,err,"open invalidFile.yaml: no such file or directory")
52+
}
53+
})
54+
t.Run("WithInvalidYAML",func(t*testing.T) {
55+
t.Parallel()
56+
tempDir:=t.TempDir()
57+
parameterFile,_:=os.CreateTemp(tempDir,"testParameterFile*.yaml")
58+
_,_=parameterFile.WriteString("region =\"bananas\"\ndisk =\"20\"\n")
59+
60+
parameterMapFromFile,err:=createParameterMapFromFile(parameterFile.Name())
61+
62+
assert.Nil(t,parameterMapFromFile)
63+
assert.EqualError(t,err,"yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `region ...` into map[string]string")
64+
65+
removeTmpDirUntilSuccess(t,tempDir)
66+
})
67+
}
68+
69+
// Need this for Windows because of a known issue with Go:
70+
// https://github.com/golang/go/issues/52986
71+
funcremoveTmpDirUntilSuccess(t*testing.T,tempDirstring) {
72+
t.Helper()
73+
t.Cleanup(func() {
74+
err:=os.RemoveAll(tempDir)
75+
forerr!=nil {
76+
err=os.RemoveAll(tempDir)
77+
}
78+
})
79+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp