@@ -71,11 +71,23 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
71
71
return
72
72
}
73
73
74
+ // staticDiagnostics is a set of diagnostics to be applied to all rendered results.
74
75
staticDiagnostics := parameterProvisionerVersionDiagnostic (tf )
75
76
77
+ // render is the function that given a set of input values, will return the
78
+ // parameter state. There is 2 rendering functions.
79
+ //
80
+ // prepareStaticPreview uses the static set of parameters saved from the template
81
+ // import. These parameters are returned on every request, and have no dynamic
82
+ // functionality. This exists for backwards compatibility with older template versions
83
+ // which have not uploaded their plan & module files.
84
+ //
85
+ // prepareDynamicPreview uses the dynamic preview engine.
76
86
var render previewFunction
77
87
major , minor , err := apiversion .Parse (tf .ProvisionerdVersion )
78
88
if err != nil || major < 1 || (major == 1 && minor < 5 ) {
89
+ // Versions < 1.5 do not upload the required files.
90
+ // Versions == "" are < 1.5, but we don't know the exact version.
79
91
staticRender , err := prepareStaticPreview (ctx , api .Database , templateVersion .ID )
80
92
if err != nil {
81
93
httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
@@ -113,7 +125,7 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
113
125
// Send an initial form state, computed without any user input.
114
126
result , diagnostics := render (ctx , map [string ]string {})
115
127
response := codersdk.DynamicParametersResponse {
116
- ID : - 1 ,
128
+ ID : - 1 , // Always start with -1.
117
129
Diagnostics : previewtypes .Diagnostics (diagnostics .Extend (staticDiagnostics )),
118
130
}
119
131
if result != nil {
@@ -138,6 +150,7 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
138
150
// The connection has been closed, so there is no one to write to
139
151
return
140
152
}
153
+
141
154
result , diagnostics := render (ctx , update .Inputs )
142
155
response := codersdk.DynamicParametersResponse {
143
156
ID : update .ID ,
@@ -158,12 +171,16 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
158
171
type previewFunction func (ctx context.Context , values map [string ]string ) (* preview.Output , hcl.Diagnostics )
159
172
160
173
func prepareDynamicPreview (ctx context.Context , rw http.ResponseWriter , db database.Store , fc * files.Cache , tf database.TemplateVersionTerraformValue , templateVersion database.TemplateVersion , user database.User ) (render previewFunction , closer func (), success bool ) {
174
+ // keep track of all files opened
161
175
openFiles := make ([]uuid.UUID , 0 )
162
176
closeFiles := func () {
163
177
for _ , it := range openFiles {
164
178
fc .Release (it )
165
179
}
166
180
}
181
+
182
+ // This defer will close the files if the function exits early without success.
183
+ // Closing the files is important to avoid having a memory leak.
167
184
defer func () {
168
185
if ! success {
169
186
closeFiles ()
@@ -182,6 +199,8 @@ func prepareDynamicPreview(ctx context.Context, rw http.ResponseWriter, db datab
182
199
return nil , nil , false
183
200
}
184
201
202
+ // Add the file first. Calling `Release` if it fails is a no-op, so this is safe.
203
+ openFiles = append (openFiles , fileID )
185
204
templateFS , err := fc .Acquire (fileCtx , fileID )
186
205
if err != nil {
187
206
httpapi .Write (ctx , rw , http .StatusNotFound , codersdk.Response {
@@ -190,14 +209,14 @@ func prepareDynamicPreview(ctx context.Context, rw http.ResponseWriter, db datab
190
209
})
191
210
return nil , nil , false
192
211
}
193
- openFiles = append (openFiles , fileID )
194
212
195
213
// Having the Terraform plan available for the evaluation engine is helpful
196
214
// for populating values from data blocks, but isn't strictly required. If
197
215
// we don't have a cached plan available, we just use an empty one instead.
198
216
plan := json .RawMessage ("{}" )
199
217
plan = tf .CachedPlan
200
218
219
+ openFiles = append (openFiles , tf .CachedModuleFiles .UUID )
201
220
if tf .CachedModuleFiles .Valid {
202
221
moduleFilesFS , err := fc .Acquire (fileCtx , tf .CachedModuleFiles .UUID )
203
222
if err != nil {
@@ -207,7 +226,6 @@ func prepareDynamicPreview(ctx context.Context, rw http.ResponseWriter, db datab
207
226
})
208
227
return nil , nil , false
209
228
}
210
- openFiles = append (openFiles , tf .CachedModuleFiles .UUID )
211
229
212
230
templateFS = files .NewOverlayFS (templateFS , []files.Overlay {{Path : ".terraform/modules" , FS : moduleFilesFS }})
213
231
}
@@ -371,7 +389,10 @@ func getWorkspaceOwnerData(
371
389
372
390
var publicKey string
373
391
g .Go (func () error {
374
- key , err := db .GetGitSSHKey (ctx , user .ID )
392
+ // The correct public key has to be sent. This will not be leaked
393
+ // unless the template leaks it.
394
+ // nolint:gocritic
395
+ key , err := db .GetGitSSHKey (dbauthz .AsSystemRestricted (ctx ), user .ID )
375
396
if err != nil {
376
397
return err
377
398
}
@@ -381,7 +402,11 @@ func getWorkspaceOwnerData(
381
402
382
403
var groupNames []string
383
404
g .Go (func () error {
384
- groups , err := db .GetGroups (ctx , database.GetGroupsParams {
405
+ // The groups need to be sent to preview. These groups are not exposed to the
406
+ // user, unless the template does it through the parameters. Regardless, we need
407
+ // the correct groups, and a user might not have read access.
408
+ // nolint:gocritic
409
+ groups , err := db .GetGroups (dbauthz .AsSystemRestricted (ctx ), database.GetGroupsParams {
385
410
OrganizationID : organizationID ,
386
411
HasMemberID : user .ID ,
387
412
})
0 commit comments