]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/config/raw_config.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / raw_config.go
CommitLineData
bae9f6d2
JC
1package config
2
3import (
4 "bytes"
5 "encoding/gob"
15c0b25d
AP
6 "errors"
7 "strconv"
bae9f6d2
JC
8 "sync"
9
15c0b25d
AP
10 "github.com/zclconf/go-cty/cty"
11 "github.com/zclconf/go-cty/cty/convert"
12
13 hcl2 "github.com/hashicorp/hcl2/hcl"
bae9f6d2
JC
14 "github.com/hashicorp/hil"
15 "github.com/hashicorp/hil/ast"
16 "github.com/mitchellh/copystructure"
17 "github.com/mitchellh/reflectwalk"
18)
19
bae9f6d2
JC
20// RawConfig is a structure that holds a piece of configuration
21// where the overall structure is unknown since it will be used
22// to configure a plugin or some other similar external component.
23//
24// RawConfigs can be interpolated with variables that come from
25// other resources, user variables, etc.
26//
27// RawConfig supports a query-like interface to request
28// information from deep within the structure.
29type RawConfig struct {
15c0b25d
AP
30 Key string
31
32 // Only _one_ of Raw and Body may be populated at a time.
33 //
34 // In the normal case, Raw is populated and Body is nil.
35 //
36 // When the experimental HCL2 parsing mode is enabled, "Body"
37 // is populated and RawConfig serves only to transport the hcl2.Body
38 // through the rest of Terraform core so we can ultimately decode it
39 // once its schema is known.
40 //
41 // Once we transition to HCL2 as the primary representation, RawConfig
42 // should be removed altogether and the hcl2.Body should be passed
43 // around directly.
44
45 Raw map[string]interface{}
46 Body hcl2.Body
47
bae9f6d2
JC
48 Interpolations []ast.Node
49 Variables map[string]InterpolatedVariable
50
51 lock sync.Mutex
52 config map[string]interface{}
53 unknownKeys []string
54}
55
56// NewRawConfig creates a new RawConfig structure and populates the
57// publicly readable struct fields.
58func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) {
59 result := &RawConfig{Raw: raw}
60 if err := result.init(); err != nil {
61 return nil, err
62 }
63
64 return result, nil
65}
66
15c0b25d
AP
67// NewRawConfigHCL2 creates a new RawConfig that is serving as a capsule
68// to transport a hcl2.Body. In this mode, the publicly-readable struct
69// fields are not populated since all operations should instead be diverted
70// to the HCL2 body.
71//
72// For a RawConfig object constructed with this function, the only valid use
73// is to later retrieve the Body value and call its own methods. Callers
74// may choose to set and then later handle the Key field, in a manner
75// consistent with how it is handled by the Value method, but the Value
76// method itself must not be used.
77//
78// This is an experimental codepath to be used only by the HCL2 config loader.
79// Non-experimental parsing should _always_ use NewRawConfig to produce a
80// fully-functional RawConfig object.
81func NewRawConfigHCL2(body hcl2.Body) *RawConfig {
82 return &RawConfig{
83 Body: body,
84 }
85}
86
bae9f6d2
JC
87// RawMap returns a copy of the RawConfig.Raw map.
88func (r *RawConfig) RawMap() map[string]interface{} {
89 r.lock.Lock()
90 defer r.lock.Unlock()
91
92 m := make(map[string]interface{})
93 for k, v := range r.Raw {
94 m[k] = v
95 }
96 return m
97}
98
99// Copy returns a copy of this RawConfig, uninterpolated.
100func (r *RawConfig) Copy() *RawConfig {
101 if r == nil {
102 return nil
103 }
104
105 r.lock.Lock()
106 defer r.lock.Unlock()
107
15c0b25d
AP
108 if r.Body != nil {
109 return NewRawConfigHCL2(r.Body)
110 }
111
bae9f6d2
JC
112 newRaw := make(map[string]interface{})
113 for k, v := range r.Raw {
114 newRaw[k] = v
115 }
116
117 result, err := NewRawConfig(newRaw)
118 if err != nil {
119 panic("copy failed: " + err.Error())
120 }
121
122 result.Key = r.Key
123 return result
124}
125
126// Value returns the value of the configuration if this configuration
127// has a Key set. If this does not have a Key set, nil will be returned.
128func (r *RawConfig) Value() interface{} {
129 if c := r.Config(); c != nil {
130 if v, ok := c[r.Key]; ok {
131 return v
132 }
133 }
134
135 r.lock.Lock()
136 defer r.lock.Unlock()
137 return r.Raw[r.Key]
138}
139
140// Config returns the entire configuration with the variables
141// interpolated from any call to Interpolate.
142//
143// If any interpolated variables are unknown (value set to
144// UnknownVariableValue), the first non-container (map, slice, etc.) element
145// will be removed from the config. The keys of unknown variables
146// can be found using the UnknownKeys function.
147//
148// By pruning out unknown keys from the configuration, the raw
149// structure will always successfully decode into its ultimate
150// structure using something like mapstructure.
151func (r *RawConfig) Config() map[string]interface{} {
152 r.lock.Lock()
153 defer r.lock.Unlock()
154 return r.config
155}
156
157// Interpolate uses the given mapping of variable values and uses
158// those as the values to replace any variables in this raw
159// configuration.
160//
161// Any prior calls to Interpolate are replaced with this one.
162//
163// If a variable key is missing, this will panic.
164func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error {
165 r.lock.Lock()
166 defer r.lock.Unlock()
167
168 config := langEvalConfig(vs)
169 return r.interpolate(func(root ast.Node) (interface{}, error) {
170 // None of the variables we need are computed, meaning we should
171 // be able to properly evaluate.
172 result, err := hil.Eval(root, config)
173 if err != nil {
174 return "", err
175 }
176
177 return result.Value, nil
178 })
179}
180
181// Merge merges another RawConfig into this one (overriding any conflicting
182// values in this config) and returns a new config. The original config
183// is not modified.
184func (r *RawConfig) Merge(other *RawConfig) *RawConfig {
185 r.lock.Lock()
186 defer r.lock.Unlock()
187
188 // Merge the raw configurations
189 raw := make(map[string]interface{})
190 for k, v := range r.Raw {
191 raw[k] = v
192 }
193 for k, v := range other.Raw {
194 raw[k] = v
195 }
196
197 // Create the result
198 result, err := NewRawConfig(raw)
199 if err != nil {
200 panic(err)
201 }
202
203 // Merge the interpolated results
204 result.config = make(map[string]interface{})
205 for k, v := range r.config {
206 result.config[k] = v
207 }
208 for k, v := range other.config {
209 result.config[k] = v
210 }
211
212 // Build the unknown keys
213 if len(r.unknownKeys) > 0 || len(other.unknownKeys) > 0 {
214 unknownKeys := make(map[string]struct{})
215 for _, k := range r.unknownKeys {
216 unknownKeys[k] = struct{}{}
217 }
218 for _, k := range other.unknownKeys {
219 unknownKeys[k] = struct{}{}
220 }
221
222 result.unknownKeys = make([]string, 0, len(unknownKeys))
223 for k, _ := range unknownKeys {
224 result.unknownKeys = append(result.unknownKeys, k)
225 }
226 }
227
228 return result
229}
230
231func (r *RawConfig) init() error {
232 r.lock.Lock()
233 defer r.lock.Unlock()
234
235 r.config = r.Raw
236 r.Interpolations = nil
237 r.Variables = nil
238
239 fn := func(node ast.Node) (interface{}, error) {
240 r.Interpolations = append(r.Interpolations, node)
241 vars, err := DetectVariables(node)
242 if err != nil {
243 return "", err
244 }
245
246 for _, v := range vars {
247 if r.Variables == nil {
248 r.Variables = make(map[string]InterpolatedVariable)
249 }
250
251 r.Variables[v.FullKey()] = v
252 }
253
254 return "", nil
255 }
256
257 walker := &interpolationWalker{F: fn}
258 if err := reflectwalk.Walk(r.Raw, walker); err != nil {
259 return err
260 }
261
262 return nil
263}
264
265func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
15c0b25d
AP
266 if r.Body != nil {
267 // For RawConfigs created for the HCL2 experiement, callers must
268 // use the HCL2 Body API directly rather than interpolating via
269 // the RawConfig.
270 return errors.New("this feature is not yet supported under the HCL2 experiment")
271 }
272
bae9f6d2
JC
273 config, err := copystructure.Copy(r.Raw)
274 if err != nil {
275 return err
276 }
277 r.config = config.(map[string]interface{})
278
279 w := &interpolationWalker{F: fn, Replace: true}
280 err = reflectwalk.Walk(r.config, w)
281 if err != nil {
282 return err
283 }
284
285 r.unknownKeys = w.unknownKeys
286 return nil
287}
288
289func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
290 if r == nil && r2 == nil {
291 return nil
292 }
293
294 if r == nil {
295 r = &RawConfig{}
296 }
297
298 rawRaw, err := copystructure.Copy(r.Raw)
299 if err != nil {
300 panic(err)
301 }
302
303 raw := rawRaw.(map[string]interface{})
304 if r2 != nil {
305 for k, v := range r2.Raw {
306 raw[k] = v
307 }
308 }
309
310 result, err := NewRawConfig(raw)
311 if err != nil {
312 panic(err)
313 }
314
315 return result
316}
317
15c0b25d
AP
318// couldBeInteger is a helper that determines if the represented value could
319// result in an integer.
320//
321// This function only works for RawConfigs that have "Key" set, meaning that
322// a single result can be produced. Calling this function will overwrite
323// the Config and Value results to be a test value.
324//
325// This function is conservative. If there is some doubt about whether the
326// result could be an integer -- for example, if it depends on a variable
327// whose type we don't know yet -- it will still return true.
328func (r *RawConfig) couldBeInteger() bool {
329 if r.Key == "" {
330 // un-keyed RawConfigs can never produce numbers
331 return false
332 }
333 if r.Body == nil {
334 // Normal path: using the interpolator in this package
335 // Interpolate with a fixed number to verify that its a number.
336 r.interpolate(func(root ast.Node) (interface{}, error) {
337 // Execute the node but transform the AST so that it returns
338 // a fixed value of "5" for all interpolations.
339 result, err := hil.Eval(
340 hil.FixedValueTransform(
341 root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}),
342 nil)
343 if err != nil {
344 return "", err
345 }
346
347 return result.Value, nil
348 })
349 _, err := strconv.ParseInt(r.Value().(string), 0, 0)
350 return err == nil
351 } else {
352 // HCL2 experiment path: using the HCL2 API via shims
353 //
354 // This path catches fewer situations because we have to assume all
355 // variables are entirely unknown in HCL2, rather than the assumption
356 // above that all variables can be numbers because names like "var.foo"
357 // are considered a single variable rather than an attribute access.
358 // This is fine in practice, because we get a definitive answer
359 // during the graph walk when we have real values to work with.
360 attrs, diags := r.Body.JustAttributes()
361 if diags.HasErrors() {
362 // This body is not just a single attribute with a value, so
363 // this can't be a number.
364 return false
365 }
366 attr, hasAttr := attrs[r.Key]
367 if !hasAttr {
368 return false
369 }
370 result, diags := hcl2EvalWithUnknownVars(attr.Expr)
371 if diags.HasErrors() {
372 // We'll conservatively assume that this error is a result of
373 // us not being ready to fully-populate the scope, and catch
374 // any further problems during the main graph walk.
375 return true
376 }
377
378 // If the result is convertable to number then we'll allow it.
379 // We do this because an unknown string is optimistically convertable
380 // to number (might be "5") but a _known_ string "hello" is not.
381 _, err := convert.Convert(result, cty.Number)
382 return err == nil
383 }
384}
385
bae9f6d2
JC
386// UnknownKeys returns the keys of the configuration that are unknown
387// because they had interpolated variables that must be computed.
388func (r *RawConfig) UnknownKeys() []string {
389 r.lock.Lock()
390 defer r.lock.Unlock()
391 return r.unknownKeys
392}
393
394// See GobEncode
395func (r *RawConfig) GobDecode(b []byte) error {
396 var data gobRawConfig
397 err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data)
398 if err != nil {
399 return err
400 }
401
402 r.Key = data.Key
403 r.Raw = data.Raw
404
405 return r.init()
406}
407
408// GobEncode is a custom Gob encoder to use so that we only include the
409// raw configuration. Interpolated variables and such are lost and the
410// tree of interpolated variables is recomputed on decode, since it is
411// referentially transparent.
412func (r *RawConfig) GobEncode() ([]byte, error) {
413 r.lock.Lock()
414 defer r.lock.Unlock()
415
416 data := gobRawConfig{
417 Key: r.Key,
418 Raw: r.Raw,
419 }
420
421 var buf bytes.Buffer
422 if err := gob.NewEncoder(&buf).Encode(data); err != nil {
423 return nil, err
424 }
425
426 return buf.Bytes(), nil
427}
428
429type gobRawConfig struct {
430 Key string
431 Raw map[string]interface{}
432}
433
434// langEvalConfig returns the evaluation configuration we use to execute.
435func langEvalConfig(vs map[string]ast.Variable) *hil.EvalConfig {
436 funcMap := make(map[string]ast.Function)
437 for k, v := range Funcs() {
438 funcMap[k] = v
439 }
440 funcMap["lookup"] = interpolationFuncLookup(vs)
441 funcMap["keys"] = interpolationFuncKeys(vs)
442 funcMap["values"] = interpolationFuncValues(vs)
443
444 return &hil.EvalConfig{
445 GlobalScope: &ast.BasicScope{
446 VarMap: vs,
447 FuncMap: funcMap,
448 },
449 }
450}