diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/raw_config.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/raw_config.go | 125 |
1 files changed, 123 insertions, 2 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/raw_config.go b/vendor/github.com/hashicorp/terraform/config/raw_config.go index f8498d8..1854a8b 100644 --- a/vendor/github.com/hashicorp/terraform/config/raw_config.go +++ b/vendor/github.com/hashicorp/terraform/config/raw_config.go | |||
@@ -3,8 +3,14 @@ package config | |||
3 | import ( | 3 | import ( |
4 | "bytes" | 4 | "bytes" |
5 | "encoding/gob" | 5 | "encoding/gob" |
6 | "errors" | ||
7 | "strconv" | ||
6 | "sync" | 8 | "sync" |
7 | 9 | ||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | "github.com/zclconf/go-cty/cty/convert" | ||
12 | |||
13 | hcl2 "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/hil" | 14 | "github.com/hashicorp/hil" |
9 | "github.com/hashicorp/hil/ast" | 15 | "github.com/hashicorp/hil/ast" |
10 | "github.com/mitchellh/copystructure" | 16 | "github.com/mitchellh/copystructure" |
@@ -27,8 +33,24 @@ const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" | |||
27 | // RawConfig supports a query-like interface to request | 33 | // RawConfig supports a query-like interface to request |
28 | // information from deep within the structure. | 34 | // information from deep within the structure. |
29 | type RawConfig struct { | 35 | type RawConfig struct { |
30 | Key string | 36 | Key string |
31 | Raw map[string]interface{} | 37 | |
38 | // Only _one_ of Raw and Body may be populated at a time. | ||
39 | // | ||
40 | // In the normal case, Raw is populated and Body is nil. | ||
41 | // | ||
42 | // When the experimental HCL2 parsing mode is enabled, "Body" | ||
43 | // is populated and RawConfig serves only to transport the hcl2.Body | ||
44 | // through the rest of Terraform core so we can ultimately decode it | ||
45 | // once its schema is known. | ||
46 | // | ||
47 | // Once we transition to HCL2 as the primary representation, RawConfig | ||
48 | // should be removed altogether and the hcl2.Body should be passed | ||
49 | // around directly. | ||
50 | |||
51 | Raw map[string]interface{} | ||
52 | Body hcl2.Body | ||
53 | |||
32 | Interpolations []ast.Node | 54 | Interpolations []ast.Node |
33 | Variables map[string]InterpolatedVariable | 55 | Variables map[string]InterpolatedVariable |
34 | 56 | ||
@@ -48,6 +70,26 @@ func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) { | |||
48 | return result, nil | 70 | return result, nil |
49 | } | 71 | } |
50 | 72 | ||
73 | // NewRawConfigHCL2 creates a new RawConfig that is serving as a capsule | ||
74 | // to transport a hcl2.Body. In this mode, the publicly-readable struct | ||
75 | // fields are not populated since all operations should instead be diverted | ||
76 | // to the HCL2 body. | ||
77 | // | ||
78 | // For a RawConfig object constructed with this function, the only valid use | ||
79 | // is to later retrieve the Body value and call its own methods. Callers | ||
80 | // may choose to set and then later handle the Key field, in a manner | ||
81 | // consistent with how it is handled by the Value method, but the Value | ||
82 | // method itself must not be used. | ||
83 | // | ||
84 | // This is an experimental codepath to be used only by the HCL2 config loader. | ||
85 | // Non-experimental parsing should _always_ use NewRawConfig to produce a | ||
86 | // fully-functional RawConfig object. | ||
87 | func NewRawConfigHCL2(body hcl2.Body) *RawConfig { | ||
88 | return &RawConfig{ | ||
89 | Body: body, | ||
90 | } | ||
91 | } | ||
92 | |||
51 | // RawMap returns a copy of the RawConfig.Raw map. | 93 | // RawMap returns a copy of the RawConfig.Raw map. |
52 | func (r *RawConfig) RawMap() map[string]interface{} { | 94 | func (r *RawConfig) RawMap() map[string]interface{} { |
53 | r.lock.Lock() | 95 | r.lock.Lock() |
@@ -69,6 +111,10 @@ func (r *RawConfig) Copy() *RawConfig { | |||
69 | r.lock.Lock() | 111 | r.lock.Lock() |
70 | defer r.lock.Unlock() | 112 | defer r.lock.Unlock() |
71 | 113 | ||
114 | if r.Body != nil { | ||
115 | return NewRawConfigHCL2(r.Body) | ||
116 | } | ||
117 | |||
72 | newRaw := make(map[string]interface{}) | 118 | newRaw := make(map[string]interface{}) |
73 | for k, v := range r.Raw { | 119 | for k, v := range r.Raw { |
74 | newRaw[k] = v | 120 | newRaw[k] = v |
@@ -223,6 +269,13 @@ func (r *RawConfig) init() error { | |||
223 | } | 269 | } |
224 | 270 | ||
225 | func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { | 271 | func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { |
272 | if r.Body != nil { | ||
273 | // For RawConfigs created for the HCL2 experiement, callers must | ||
274 | // use the HCL2 Body API directly rather than interpolating via | ||
275 | // the RawConfig. | ||
276 | return errors.New("this feature is not yet supported under the HCL2 experiment") | ||
277 | } | ||
278 | |||
226 | config, err := copystructure.Copy(r.Raw) | 279 | config, err := copystructure.Copy(r.Raw) |
227 | if err != nil { | 280 | if err != nil { |
228 | return err | 281 | return err |
@@ -268,6 +321,74 @@ func (r *RawConfig) merge(r2 *RawConfig) *RawConfig { | |||
268 | return result | 321 | return result |
269 | } | 322 | } |
270 | 323 | ||
324 | // couldBeInteger is a helper that determines if the represented value could | ||
325 | // result in an integer. | ||
326 | // | ||
327 | // This function only works for RawConfigs that have "Key" set, meaning that | ||
328 | // a single result can be produced. Calling this function will overwrite | ||
329 | // the Config and Value results to be a test value. | ||
330 | // | ||
331 | // This function is conservative. If there is some doubt about whether the | ||
332 | // result could be an integer -- for example, if it depends on a variable | ||
333 | // whose type we don't know yet -- it will still return true. | ||
334 | func (r *RawConfig) couldBeInteger() bool { | ||
335 | if r.Key == "" { | ||
336 | // un-keyed RawConfigs can never produce numbers | ||
337 | return false | ||
338 | } | ||
339 | if r.Body == nil { | ||
340 | // Normal path: using the interpolator in this package | ||
341 | // Interpolate with a fixed number to verify that its a number. | ||
342 | r.interpolate(func(root ast.Node) (interface{}, error) { | ||
343 | // Execute the node but transform the AST so that it returns | ||
344 | // a fixed value of "5" for all interpolations. | ||
345 | result, err := hil.Eval( | ||
346 | hil.FixedValueTransform( | ||
347 | root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}), | ||
348 | nil) | ||
349 | if err != nil { | ||
350 | return "", err | ||
351 | } | ||
352 | |||
353 | return result.Value, nil | ||
354 | }) | ||
355 | _, err := strconv.ParseInt(r.Value().(string), 0, 0) | ||
356 | return err == nil | ||
357 | } else { | ||
358 | // HCL2 experiment path: using the HCL2 API via shims | ||
359 | // | ||
360 | // This path catches fewer situations because we have to assume all | ||
361 | // variables are entirely unknown in HCL2, rather than the assumption | ||
362 | // above that all variables can be numbers because names like "var.foo" | ||
363 | // are considered a single variable rather than an attribute access. | ||
364 | // This is fine in practice, because we get a definitive answer | ||
365 | // during the graph walk when we have real values to work with. | ||
366 | attrs, diags := r.Body.JustAttributes() | ||
367 | if diags.HasErrors() { | ||
368 | // This body is not just a single attribute with a value, so | ||
369 | // this can't be a number. | ||
370 | return false | ||
371 | } | ||
372 | attr, hasAttr := attrs[r.Key] | ||
373 | if !hasAttr { | ||
374 | return false | ||
375 | } | ||
376 | result, diags := hcl2EvalWithUnknownVars(attr.Expr) | ||
377 | if diags.HasErrors() { | ||
378 | // We'll conservatively assume that this error is a result of | ||
379 | // us not being ready to fully-populate the scope, and catch | ||
380 | // any further problems during the main graph walk. | ||
381 | return true | ||
382 | } | ||
383 | |||
384 | // If the result is convertable to number then we'll allow it. | ||
385 | // We do this because an unknown string is optimistically convertable | ||
386 | // to number (might be "5") but a _known_ string "hello" is not. | ||
387 | _, err := convert.Convert(result, cty.Number) | ||
388 | return err == nil | ||
389 | } | ||
390 | } | ||
391 | |||
271 | // UnknownKeys returns the keys of the configuration that are unknown | 392 | // UnknownKeys returns the keys of the configuration that are unknown |
272 | // because they had interpolated variables that must be computed. | 393 | // because they had interpolated variables that must be computed. |
273 | func (r *RawConfig) UnknownKeys() []string { | 394 | func (r *RawConfig) UnknownKeys() []string { |