@@ -120,87 +120,11 @@ func create() *cobra.Command {
120
120
schedSpec = ptr .Ref (sched .String ())
121
121
}
122
122
123
- templateVersion , err := client .TemplateVersion (cmd .Context (), template .ActiveVersionID )
124
- if err != nil {
125
- return err
126
- }
127
- parameterSchemas , err := client .TemplateVersionSchema (cmd .Context (), templateVersion .ID )
128
- if err != nil {
129
- return err
130
- }
131
-
132
- // parameterMapFromFile can be nil if parameter file is not specified
133
- var parameterMapFromFile map [string ]string
134
- if parameterFile != "" {
135
- _ , _ = fmt .Fprintln (cmd .OutOrStdout (), cliui .Styles .Paragraph .Render ("Attempting to read the variables from the parameter file." )+ "\r \n " )
136
- parameterMapFromFile , err = createParameterMapFromFile (parameterFile )
137
- if err != nil {
138
- return err
139
- }
140
- }
141
-
142
- disclaimerPrinted := false
143
- parameters := make ([]codersdk.CreateParameterRequest , 0 )
144
- for _ , parameterSchema := range parameterSchemas {
145
- if ! parameterSchema .AllowOverrideSource {
146
- continue
147
- }
148
- if ! disclaimerPrinted {
149
- _ , _ = 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 " )
150
- disclaimerPrinted = true
151
- }
152
- parameterValue , err := getParameterValueFromMapOrInput (cmd , parameterMapFromFile , parameterSchema )
153
- if err != nil {
154
- return err
155
- }
156
- parameters = append (parameters , codersdk.CreateParameterRequest {
157
- Name : parameterSchema .Name ,
158
- SourceValue : parameterValue ,
159
- SourceScheme : codersdk .ParameterSourceSchemeData ,
160
- DestinationScheme : parameterSchema .DefaultDestinationScheme ,
161
- })
162
- }
163
- _ , _ = fmt .Fprintln (cmd .OutOrStdout ())
164
-
165
- // Run a dry-run with the given parameters to check correctness
166
- after := time .Now ()
167
- dryRun , err := client .CreateTemplateVersionDryRun (cmd .Context (), templateVersion .ID , codersdk.CreateTemplateVersionDryRunRequest {
168
- WorkspaceName : workspaceName ,
169
- ParameterValues : parameters ,
170
- })
171
- if err != nil {
172
- return xerrors .Errorf ("begin workspace dry-run: %w" , err )
173
- }
174
- _ , _ = fmt .Fprintln (cmd .OutOrStdout (), "Planning workspace..." )
175
- err = cliui .ProvisionerJob (cmd .Context (), cmd .OutOrStdout (), cliui.ProvisionerJobOptions {
176
- Fetch : func () (codersdk.ProvisionerJob , error ) {
177
- return client .TemplateVersionDryRun (cmd .Context (), templateVersion .ID , dryRun .ID )
178
- },
179
- Cancel : func () error {
180
- return client .CancelTemplateVersionDryRun (cmd .Context (), templateVersion .ID , dryRun .ID )
181
- },
182
- Logs : func () (<- chan codersdk.ProvisionerJobLog , error ) {
183
- return client .TemplateVersionDryRunLogsAfter (cmd .Context (), templateVersion .ID , dryRun .ID , after )
184
- },
185
- // Don't show log output for the dry-run unless there's an error.
186
- Silent : true ,
187
- })
188
- if err != nil {
189
- // TODO (Dean): reprompt for parameter values if we deem it to
190
- // be a validation error
191
- return xerrors .Errorf ("dry-run workspace: %w" , err )
192
- }
193
-
194
- resources , err := client .TemplateVersionDryRunResources (cmd .Context (), templateVersion .ID , dryRun .ID )
195
- if err != nil {
196
- return xerrors .Errorf ("get workspace dry-run resources: %w" , err )
197
- }
198
-
199
- err = cliui .WorkspaceResources (cmd .OutOrStdout (), resources , cliui.WorkspaceResourcesOptions {
200
- WorkspaceName : workspaceName ,
201
- // Since agent's haven't connected yet, hiding this makes more sense.
202
- HideAgentState : true ,
203
- Title : "Workspace Preview" ,
123
+ parameters , err := prepWorkspaceBuild (cmd , client , prepWorkspaceBuildArgs {
124
+ Template : template ,
125
+ ExistingParams : []codersdk.Parameter {},
126
+ ParameterFile : parameterFile ,
127
+ NewWorkspaceName : workspaceName ,
204
128
})
205
129
if err != nil {
206
130
return err
@@ -214,6 +138,7 @@ func create() *cobra.Command {
214
138
return err
215
139
}
216
140
141
+ after := time .Now ()
217
142
workspace , err := client .CreateWorkspace (cmd .Context (), organization .ID , codersdk.CreateWorkspaceRequest {
218
143
TemplateID : template .ID ,
219
144
Name : workspaceName ,
@@ -242,3 +167,118 @@ func create() *cobra.Command {
242
167
cliflag .DurationVarP (cmd .Flags (), & stopAfter , "stop-after" , "" , "CODER_WORKSPACE_STOP_AFTER" , 8 * time .Hour , "Specify a duration after which the workspace should shut down (e.g. 8h)." )
243
168
return cmd
244
169
}
170
+
171
+ type prepWorkspaceBuildArgs struct {
172
+ Template codersdk.Template
173
+ ExistingParams []codersdk.Parameter
174
+ ParameterFile string
175
+ NewWorkspaceName string
176
+ }
177
+
178
+ // prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version.
179
+ // Any missing params will be prompted to the user.
180
+ func prepWorkspaceBuild (cmd * cobra.Command , client * codersdk.Client , args prepWorkspaceBuildArgs ) ([]codersdk.CreateParameterRequest , error ) {
181
+ ctx := cmd .Context ()
182
+ templateVersion , err := client .TemplateVersion (ctx , args .Template .ActiveVersionID )
183
+ if err != nil {
184
+ return nil , err
185
+ }
186
+ parameterSchemas , err := client .TemplateVersionSchema (ctx , templateVersion .ID )
187
+ if err != nil {
188
+ return nil , err
189
+ }
190
+
191
+ // parameterMapFromFile can be nil if parameter file is not specified
192
+ var parameterMapFromFile map [string ]string
193
+ useParamFile := false
194
+ if args .ParameterFile != "" {
195
+ useParamFile = true
196
+ _ , _ = fmt .Fprintln (cmd .OutOrStdout (), cliui .Styles .Paragraph .Render ("Attempting to read the variables from the parameter file." )+ "\r \n " )
197
+ parameterMapFromFile , err = createParameterMapFromFile (args .ParameterFile )
198
+ if err != nil {
199
+ return nil , err
200
+ }
201
+ }
202
+ disclaimerPrinted := false
203
+ parameters := make ([]codersdk.CreateParameterRequest , 0 )
204
+ PromptParamLoop:
205
+ for _ , parameterSchema := range parameterSchemas {
206
+ if ! parameterSchema .AllowOverrideSource {
207
+ continue
208
+ }
209
+ if ! disclaimerPrinted {
210
+ _ , _ = 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 " )
211
+ disclaimerPrinted = true
212
+ }
213
+
214
+ // Param file is all or nothing
215
+ if ! useParamFile {
216
+ for _ , e := range args .ExistingParams {
217
+ if e .Name == parameterSchema .Name {
218
+ // If the param already exists, we do not need to prompt it again.
219
+ // The workspace scope will reuse params for each build.
220
+ continue PromptParamLoop
221
+ }
222
+ }
223
+ }
224
+
225
+ parameterValue , err := getParameterValueFromMapOrInput (cmd , parameterMapFromFile , parameterSchema )
226
+ if err != nil {
227
+ return nil , err
228
+ }
229
+
230
+ parameters = append (parameters , codersdk.CreateParameterRequest {
231
+ Name : parameterSchema .Name ,
232
+ SourceValue : parameterValue ,
233
+ SourceScheme : codersdk .ParameterSourceSchemeData ,
234
+ DestinationScheme : parameterSchema .DefaultDestinationScheme ,
235
+ })
236
+ }
237
+ _ , _ = fmt .Fprintln (cmd .OutOrStdout ())
238
+
239
+ // Run a dry-run with the given parameters to check correctness
240
+ after := time .Now ()
241
+ dryRun , err := client .CreateTemplateVersionDryRun (cmd .Context (), templateVersion .ID , codersdk.CreateTemplateVersionDryRunRequest {
242
+ WorkspaceName : args .NewWorkspaceName ,
243
+ ParameterValues : parameters ,
244
+ })
245
+ if err != nil {
246
+ return nil , xerrors .Errorf ("begin workspace dry-run: %w" , err )
247
+ }
248
+ _ , _ = fmt .Fprintln (cmd .OutOrStdout (), "Planning workspace..." )
249
+ err = cliui .ProvisionerJob (cmd .Context (), cmd .OutOrStdout (), cliui.ProvisionerJobOptions {
250
+ Fetch : func () (codersdk.ProvisionerJob , error ) {
251
+ return client .TemplateVersionDryRun (cmd .Context (), templateVersion .ID , dryRun .ID )
252
+ },
253
+ Cancel : func () error {
254
+ return client .CancelTemplateVersionDryRun (cmd .Context (), templateVersion .ID , dryRun .ID )
255
+ },
256
+ Logs : func () (<- chan codersdk.ProvisionerJobLog , error ) {
257
+ return client .TemplateVersionDryRunLogsAfter (cmd .Context (), templateVersion .ID , dryRun .ID , after )
258
+ },
259
+ // Don't show log output for the dry-run unless there's an error.
260
+ Silent : true ,
261
+ })
262
+ if err != nil {
263
+ // TODO (Dean): reprompt for parameter values if we deem it to
264
+ // be a validation error
265
+ return nil , xerrors .Errorf ("dry-run workspace: %w" , err )
266
+ }
267
+
268
+ resources , err := client .TemplateVersionDryRunResources (cmd .Context (), templateVersion .ID , dryRun .ID )
269
+ if err != nil {
270
+ return nil , xerrors .Errorf ("get workspace dry-run resources: %w" , err )
271
+ }
272
+
273
+ err = cliui .WorkspaceResources (cmd .OutOrStdout (), resources , cliui.WorkspaceResourcesOptions {
274
+ WorkspaceName : args .NewWorkspaceName ,
275
+ // Since agents haven't connected yet, hiding this makes more sense.
276
+ HideAgentState : true ,
277
+ Title : "Workspace Preview" ,
278
+ })
279
+ if err != nil {
280
+ return nil , err
281
+ }
282
+
283
+ return parameters , nil
284
+ }
0 commit comments