@@ -21,6 +21,7 @@ import (
21
21
"github.com/hashicorp/hcl/v2/hclsyntax"
22
22
"github.com/hashicorp/terraform-config-inspect/tfconfig"
23
23
"github.com/zclconf/go-cty/cty"
24
+ "golang.org/x/exp/maps"
24
25
"golang.org/x/xerrors"
25
26
26
27
"cdr.dev/slog"
@@ -30,11 +31,25 @@ import (
30
31
// introducing a circular dependency
31
32
const maxFileSizeBytes = 10 * (10 << 20 ) // 10 MB
32
33
34
+ // hclparse.Parser by default keeps track of all files it has ever parsed
35
+ // by filename. By re-using the same parser instance we can avoid repeating
36
+ // previous work.
37
+ // NOTE: we can only do this if we _just_ use ParseHCLFile().
38
+ // The ParseHCL() method allows specifying the filename directly, which allows
39
+ // easily getting previously cached results. Whenever we parse HCL files from disk
40
+ // we do so in a unique file path.
41
+ // See: provisionersdk/session.go#L51
42
+ var hclFileParser ParseHCLFiler = hclparse .NewParser ()
43
+
44
+ type ParseHCLFiler interface {
45
+ ParseHCLFile (filename string ) (* hcl.File , hcl.Diagnostics )
46
+ }
47
+
33
48
// WorkspaceTags extracts tags from coder_workspace_tags data sources defined in module.
34
49
// Note that this only returns the lexical values of the data source, and does not
35
50
// evaluate variables and such. To do this, see evalProvisionerTags below.
36
- func WorkspaceTags (ctx context. Context , module * tfconfig.Module ) (map [string ]string , error ) {
37
- workspaceTags := map [string ]string {}
51
+ func WorkspaceTags (module * tfconfig.Module ) (map [string ]string , error ) {
52
+ tags := map [string ]string {}
38
53
39
54
for _ , dataResource := range module .DataResources {
40
55
if dataResource .Type != "coder_workspace_tags" {
@@ -43,13 +58,12 @@ func WorkspaceTags(ctx context.Context, module *tfconfig.Module) (map[string]str
43
58
44
59
var file * hcl.File
45
60
var diags hcl.Diagnostics
46
- parser := hclparse .NewParser ()
47
61
48
62
if ! strings .HasSuffix (dataResource .Pos .Filename , ".tf" ) {
49
63
continue
50
64
}
51
65
// We know in which HCL file is the data resource defined.
52
- file , diags = parser .ParseHCLFile (dataResource .Pos .Filename )
66
+ file , diags = hclFileParser .ParseHCLFile (dataResource .Pos .Filename )
53
67
if diags .HasErrors () {
54
68
return nil , xerrors .Errorf ("can't parse the resource file: %s" , diags .Error ())
55
69
}
@@ -99,14 +113,14 @@ func WorkspaceTags(ctx context.Context, module *tfconfig.Module) (map[string]str
99
113
return nil , xerrors .Errorf ("can't preview the resource file: %v" , err )
100
114
}
101
115
102
- if _ , ok := workspaceTags [key ]; ok {
116
+ if _ , ok := tags [key ]; ok {
103
117
return nil , xerrors .Errorf (`workspace tag %q is defined multiple times` , key )
104
118
}
105
- workspaceTags [key ] = value
119
+ tags [key ] = value
106
120
}
107
121
}
108
122
}
109
- return workspaceTags , nil
123
+ return tags , nil
110
124
}
111
125
112
126
// WorkspaceTagDefaultsFromFile extracts the default values for a `coder_workspace_tags` resource from the given
@@ -131,16 +145,20 @@ func WorkspaceTagDefaultsFromFile(ctx context.Context, logger slog.Logger, file
131
145
132
146
// This only gets us the expressions. We need to evaluate them.
133
147
// Example: var.region -> "us"
134
- tags , err = WorkspaceTags (ctx , module )
148
+ tags , err = WorkspaceTags (module )
135
149
if err != nil {
136
150
return nil , xerrors .Errorf ("extract workspace tags: %w" , err )
137
151
}
138
152
139
153
// To evaluate the expressions, we need to load the default values for
140
154
// variables and parameters.
141
- varsDefaults , paramsDefaults , err := loadDefaults (module )
155
+ varsDefaults , err := loadVarsDefaults (maps .Values (module .Variables ))
156
+ if err != nil {
157
+ return nil , xerrors .Errorf ("load variable defaults: %w" , err )
158
+ }
159
+ paramsDefaults , err := loadParamsDefaults (maps .Values (module .DataResources ))
142
160
if err != nil {
143
- return nil , xerrors .Errorf ("load defaults: %w" , err )
161
+ return nil , xerrors .Errorf ("load parameter defaults: %w" , err )
144
162
}
145
163
146
164
// Evaluate the tags expressions given the inputs.
@@ -205,46 +223,53 @@ func loadModuleFromFile(file []byte, mimetype string) (module *tfconfig.Module,
205
223
return module , cleanup , nil
206
224
}
207
225
208
- // loadDefaults inspects the given module and returns the default values for
209
- // all variables and coder_parameter data sources referenced there.
210
- func loadDefaults (module * tfconfig.Module ) (varsDefaults map [string ]string , paramsDefaults map [string ]string , err error ) {
211
- // iterate through module.Variables to get the default values for all
226
+ // loadVarsDefaults returns the default values for all variables passed to it.
227
+ func loadVarsDefaults (variables []* tfconfig.Variable ) (map [string ]string , error ) {
228
+ // iterate through vars to get the default values for all
212
229
// variables.
213
- varsDefaults = make (map [string ]string )
214
- for _ , v := range module .Variables {
230
+ m := make (map [string ]string )
231
+ for _ , v := range variables {
232
+ if v == nil {
233
+ continue
234
+ }
215
235
sv , err := interfaceToString (v .Default )
216
236
if err != nil {
217
- return nil , nil , xerrors .Errorf ("can't convert variable default value to string: %v" , err )
237
+ return nil , xerrors .Errorf ("can't convert variable default value to string: %v" , err )
218
238
}
219
- varsDefaults [v .Name ] = strings .Trim (sv , `"` )
239
+ m [v .Name ] = strings .Trim (sv , `"` )
220
240
}
241
+ return m , nil
242
+ }
243
+
244
+ // loadParamsDefaults returns the default values of all coder_parameter data sources data sources provided.
245
+ func loadParamsDefaults (dataSources []* tfconfig.Resource ) (map [string ]string , error ) {
246
+ defaultsM := make (map [string ]string )
247
+ for _ , dataResource := range dataSources {
248
+ if dataResource == nil {
249
+ continue
250
+ }
221
251
222
- // iterate through module.DataResources to get the default values for all
223
- // coder_parameter data resources.
224
- paramsDefaults = make (map [string ]string )
225
- for _ , dataResource := range module .DataResources {
226
252
if dataResource .Type != "coder_parameter" {
227
253
continue
228
254
}
229
255
230
256
var file * hcl.File
231
257
var diags hcl.Diagnostics
232
- parser := hclparse .NewParser ()
233
258
234
259
if ! strings .HasSuffix (dataResource .Pos .Filename , ".tf" ) {
235
260
continue
236
261
}
237
262
238
263
// We know in which HCL file is the data resource defined.
239
- file , diags = parser .ParseHCLFile (dataResource .Pos .Filename )
264
+ file , diags = hclFileParser .ParseHCLFile (dataResource .Pos .Filename )
240
265
if diags .HasErrors () {
241
- return nil , nil , xerrors .Errorf ("can't parse the resource file %q: %s" , dataResource .Pos .Filename , diags .Error ())
266
+ return nil , xerrors .Errorf ("can't parse the resource file %q: %s" , dataResource .Pos .Filename , diags .Error ())
242
267
}
243
268
244
269
// Parse root to find "coder_parameter".
245
270
content , _ , diags := file .Body .PartialContent (rootTemplateSchema )
246
271
if diags .HasErrors () {
247
- return nil , nil , xerrors .Errorf ("can't parse the resource file: %s" , diags .Error ())
272
+ return nil , xerrors .Errorf ("can't parse the resource file: %s" , diags .Error ())
248
273
}
249
274
250
275
// Iterate over blocks to locate the exact "coder_parameter" data resource.
@@ -256,22 +281,22 @@ func loadDefaults(module *tfconfig.Module) (varsDefaults map[string]string, para
256
281
// Parse "coder_parameter" to find the default value.
257
282
resContent , _ , diags := block .Body .PartialContent (coderParameterSchema )
258
283
if diags .HasErrors () {
259
- return nil , nil , xerrors .Errorf (`can't parse the coder_parameter: %s` , diags .Error ())
284
+ return nil , xerrors .Errorf (`can't parse the coder_parameter: %s` , diags .Error ())
260
285
}
261
286
262
287
if _ , ok := resContent .Attributes ["default" ]; ! ok {
263
- paramsDefaults [dataResource .Name ] = ""
288
+ defaultsM [dataResource .Name ] = ""
264
289
} else {
265
290
expr := resContent .Attributes ["default" ].Expr
266
291
value , err := previewFileContent (expr .Range ())
267
292
if err != nil {
268
- return nil , nil , xerrors .Errorf ("can't preview the resource file: %v" , err )
293
+ return nil , xerrors .Errorf ("can't preview the resource file: %v" , err )
269
294
}
270
- paramsDefaults [dataResource .Name ] = strings .Trim (value , `"` )
295
+ defaultsM [dataResource .Name ] = strings .Trim (value , `"` )
271
296
}
272
297
}
273
298
}
274
- return varsDefaults , paramsDefaults , nil
299
+ return defaultsM , nil
275
300
}
276
301
277
302
// EvalProvisionerTags evaluates the given workspaceTags based on the given
0 commit comments