@@ -36,7 +36,7 @@ import (
36
36
// @Param user path string true "Template version ID" format(uuid)
37
37
// @Param templateversion path string true "Template version ID" format(uuid)
38
38
// @Success 101
39
- // @Router /users/{user}/ templateversions/{templateversion}/parameters [get]
39
+ // @Router /templateversions/{templateversion}/dynamic- parameters [get]
40
40
func (api * API ) templateVersionDynamicParameters (rw http.ResponseWriter , r * http.Request ) {
41
41
ctx := r .Context ()
42
42
templateVersion := httpmw .TemplateVersionParam (r )
@@ -77,12 +77,12 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
77
77
}
78
78
}
79
79
80
- type previewFunction func (ctx context.Context , values map [string ]string ) (* preview.Output , hcl.Diagnostics )
80
+ type previewFunction func (ctx context.Context , ownerID uuid. UUID , values map [string ]string ) (* preview.Output , hcl.Diagnostics )
81
81
82
82
func (api * API ) handleDynamicParameters (rw http.ResponseWriter , r * http.Request , tf database.TemplateVersionTerraformValue , templateVersion database.TemplateVersion ) {
83
83
var (
84
- ctx = r .Context ()
85
- user = httpmw .UserParam (r )
84
+ ctx = r .Context ()
85
+ apikey = httpmw .APIKey (r )
86
86
)
87
87
88
88
// nolint:gocritic // We need to fetch the templates files for the Terraform
@@ -130,7 +130,7 @@ func (api *API) handleDynamicParameters(rw http.ResponseWriter, r *http.Request,
130
130
templateFS = files .NewOverlayFS (templateFS , []files.Overlay {{Path : ".terraform/modules" , FS : moduleFilesFS }})
131
131
}
132
132
133
- owner , err := getWorkspaceOwnerData (ctx , api .Database , user , templateVersion .OrganizationID )
133
+ owner , err := getWorkspaceOwnerData (ctx , api .Database , apikey . UserID , templateVersion .OrganizationID )
134
134
if err != nil {
135
135
httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
136
136
Message : "Internal error fetching workspace owner." ,
@@ -145,10 +145,46 @@ func (api *API) handleDynamicParameters(rw http.ResponseWriter, r *http.Request,
145
145
Owner : owner ,
146
146
}
147
147
148
- api .handleParameterWebsocket (rw , r , func (ctx context.Context , values map [string ]string ) (* preview.Output , hcl.Diagnostics ) {
148
+ // failedOwners keeps track of which owners failed to fetch from the database.
149
+ // This prevents db spam on repeated requests for the same failed owner.
150
+ failedOwners := make (map [uuid.UUID ]error )
151
+ failedOwnerDiag := hcl.Diagnostics {
152
+ {
153
+ Severity : hcl .DiagError ,
154
+ Summary : "Failed to fetch workspace owner" ,
155
+ Detail : "Please check your permissions or the user may not exist." ,
156
+ Extra : previewtypes.DiagnosticExtra {
157
+ Code : "owner_not_found" ,
158
+ },
159
+ },
160
+ }
161
+
162
+ api .handleParameterWebsocket (rw , r , apikey .UserID , func (ctx context.Context , ownerID uuid.UUID , values map [string ]string ) (* preview.Output , hcl.Diagnostics ) {
163
+ if ownerID == uuid .Nil {
164
+ // Default to the authenticated user
165
+ // Nice for testing
166
+ ownerID = apikey .UserID
167
+ }
168
+
169
+ if _ , ok := failedOwners [ownerID ]; ok {
170
+ // If it has failed once, assume it will fail always.
171
+ // Re-open the websocket to try again.
172
+ return nil , failedOwnerDiag
173
+ }
174
+
149
175
// Update the input values with the new values.
150
- // The rest of the input is unchanged.
151
176
input .ParameterValues = values
177
+
178
+ // Update the owner if there is a change
179
+ if input .Owner .ID != ownerID .String () {
180
+ owner , err = getWorkspaceOwnerData (ctx , api .Database , ownerID , templateVersion .OrganizationID )
181
+ if err != nil {
182
+ failedOwners [ownerID ] = err
183
+ return nil , failedOwnerDiag
184
+ }
185
+ input .Owner = owner
186
+ }
187
+
152
188
return preview .Preview (ctx , input , templateFS )
153
189
})
154
190
}
@@ -239,7 +275,7 @@ func (api *API) handleStaticParameters(rw http.ResponseWriter, r *http.Request,
239
275
params = append (params , param )
240
276
}
241
277
242
- api .handleParameterWebsocket (rw , r , func (_ context.Context , values map [string ]string ) (* preview.Output , hcl.Diagnostics ) {
278
+ api .handleParameterWebsocket (rw , r , uuid . Nil , func (_ context.Context , _ uuid. UUID , values map [string ]string ) (* preview.Output , hcl.Diagnostics ) {
243
279
for i := range params {
244
280
param := & params [i ]
245
281
paramValue , ok := values [param .Name ]
@@ -264,7 +300,7 @@ func (api *API) handleStaticParameters(rw http.ResponseWriter, r *http.Request,
264
300
})
265
301
}
266
302
267
- func (api * API ) handleParameterWebsocket (rw http.ResponseWriter , r * http.Request , render previewFunction ) {
303
+ func (api * API ) handleParameterWebsocket (rw http.ResponseWriter , r * http.Request , ownerID uuid. UUID , render previewFunction ) {
268
304
ctx , cancel := context .WithTimeout (r .Context (), 30 * time .Minute )
269
305
defer cancel ()
270
306
@@ -284,7 +320,7 @@ func (api *API) handleParameterWebsocket(rw http.ResponseWriter, r *http.Request
284
320
)
285
321
286
322
// Send an initial form state, computed without any user input.
287
- result , diagnostics := render (ctx , map [string ]string {})
323
+ result , diagnostics := render (ctx , ownerID , map [string ]string {})
288
324
response := codersdk.DynamicParametersResponse {
289
325
ID : - 1 , // Always start with -1.
290
326
Diagnostics : db2sdk .HCLDiagnostics (diagnostics ),
@@ -312,7 +348,7 @@ func (api *API) handleParameterWebsocket(rw http.ResponseWriter, r *http.Request
312
348
return
313
349
}
314
350
315
- result , diagnostics := render (ctx , update .Inputs )
351
+ result , diagnostics := render (ctx , update .OwnerID , update . Inputs )
316
352
response := codersdk.DynamicParametersResponse {
317
353
ID : update .ID ,
318
354
Diagnostics : db2sdk .HCLDiagnostics (diagnostics ),
@@ -332,17 +368,24 @@ func (api *API) handleParameterWebsocket(rw http.ResponseWriter, r *http.Request
332
368
func getWorkspaceOwnerData (
333
369
ctx context.Context ,
334
370
db database.Store ,
335
- user database. User ,
371
+ ownerID uuid. UUID ,
336
372
organizationID uuid.UUID ,
337
373
) (previewtypes.WorkspaceOwner , error ) {
338
374
var g errgroup.Group
339
375
376
+ // TODO: @emyrk we should only need read access on the org member, not the
377
+ // site wide user object. Figure out a better way to handle this.
378
+ user , err := db .GetUserByID (ctx , ownerID )
379
+ if err != nil {
380
+ return previewtypes.WorkspaceOwner {}, xerrors .Errorf ("fetch user: %w" , err )
381
+ }
382
+
340
383
var ownerRoles []previewtypes.WorkspaceOwnerRBACRole
341
384
g .Go (func () error {
342
385
// nolint:gocritic // This is kind of the wrong query to use here, but it
343
386
// matches how the provisioner currently works. We should figure out
344
387
// something that needs less escalation but has the correct behavior.
345
- row , err := db .GetAuthorizationUserRoles (dbauthz .AsSystemRestricted (ctx ), user . ID )
388
+ row , err := db .GetAuthorizationUserRoles (dbauthz .AsSystemRestricted (ctx ), ownerID )
346
389
if err != nil {
347
390
return err
348
391
}
@@ -372,7 +415,7 @@ func getWorkspaceOwnerData(
372
415
// The correct public key has to be sent. This will not be leaked
373
416
// unless the template leaks it.
374
417
// nolint:gocritic
375
- key , err := db .GetGitSSHKey (dbauthz .AsSystemRestricted (ctx ), user . ID )
418
+ key , err := db .GetGitSSHKey (dbauthz .AsSystemRestricted (ctx ), ownerID )
376
419
if err != nil {
377
420
return err
378
421
}
@@ -388,7 +431,7 @@ func getWorkspaceOwnerData(
388
431
// nolint:gocritic
389
432
groups , err := db .GetGroups (dbauthz .AsSystemRestricted (ctx ), database.GetGroupsParams {
390
433
OrganizationID : organizationID ,
391
- HasMemberID : user . ID ,
434
+ HasMemberID : ownerID ,
392
435
})
393
436
if err != nil {
394
437
return err
@@ -400,7 +443,7 @@ func getWorkspaceOwnerData(
400
443
return nil
401
444
})
402
445
403
- err : = g .Wait ()
446
+ err = g .Wait ()
404
447
if err != nil {
405
448
return previewtypes.WorkspaceOwner {}, err
406
449
}
0 commit comments