]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / hcl2shim / values.go
1 package hcl2shim
2
3 import (
4 "fmt"
5 "math/big"
6
7 "github.com/hashicorp/hil/ast"
8 "github.com/zclconf/go-cty/cty"
9
10 "github.com/hashicorp/terraform/configs/configschema"
11 )
12
13 // UnknownVariableValue is a sentinel value that can be used
14 // to denote that the value of a variable is unknown at this time.
15 // RawConfig uses this information to build up data about
16 // unknown keys.
17 const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
18
19 // ConfigValueFromHCL2Block is like ConfigValueFromHCL2 but it works only for
20 // known object values and uses the provided block schema to perform some
21 // additional normalization to better mimic the shape of value that the old
22 // HCL1/HIL-based codepaths would've produced.
23 //
24 // In particular, it discards the collections that we use to represent nested
25 // blocks (other than NestingSingle) if they are empty, which better mimics
26 // the HCL1 behavior because HCL1 had no knowledge of the schema and so didn't
27 // know that an unspecified block _could_ exist.
28 //
29 // The given object value must conform to the schema's implied type or this
30 // function will panic or produce incorrect results.
31 //
32 // This is primarily useful for the final transition from new-style values to
33 // terraform.ResourceConfig before calling to a legacy provider, since
34 // helper/schema (the old provider SDK) is particularly sensitive to these
35 // subtle differences within its validation code.
36 func ConfigValueFromHCL2Block(v cty.Value, schema *configschema.Block) map[string]interface{} {
37 if v.IsNull() {
38 return nil
39 }
40 if !v.IsKnown() {
41 panic("ConfigValueFromHCL2Block used with unknown value")
42 }
43 if !v.Type().IsObjectType() {
44 panic(fmt.Sprintf("ConfigValueFromHCL2Block used with non-object value %#v", v))
45 }
46
47 atys := v.Type().AttributeTypes()
48 ret := make(map[string]interface{})
49
50 for name := range schema.Attributes {
51 if _, exists := atys[name]; !exists {
52 continue
53 }
54
55 av := v.GetAttr(name)
56 if av.IsNull() {
57 // Skip nulls altogether, to better mimic how HCL1 would behave
58 continue
59 }
60 ret[name] = ConfigValueFromHCL2(av)
61 }
62
63 for name, blockS := range schema.BlockTypes {
64 if _, exists := atys[name]; !exists {
65 continue
66 }
67 bv := v.GetAttr(name)
68 if !bv.IsKnown() {
69 ret[name] = UnknownVariableValue
70 continue
71 }
72 if bv.IsNull() {
73 continue
74 }
75
76 switch blockS.Nesting {
77
78 case configschema.NestingSingle, configschema.NestingGroup:
79 ret[name] = ConfigValueFromHCL2Block(bv, &blockS.Block)
80
81 case configschema.NestingList, configschema.NestingSet:
82 l := bv.LengthInt()
83 if l == 0 {
84 // skip empty collections to better mimic how HCL1 would behave
85 continue
86 }
87
88 elems := make([]interface{}, 0, l)
89 for it := bv.ElementIterator(); it.Next(); {
90 _, ev := it.Element()
91 if !ev.IsKnown() {
92 elems = append(elems, UnknownVariableValue)
93 continue
94 }
95 elems = append(elems, ConfigValueFromHCL2Block(ev, &blockS.Block))
96 }
97 ret[name] = elems
98
99 case configschema.NestingMap:
100 if bv.LengthInt() == 0 {
101 // skip empty collections to better mimic how HCL1 would behave
102 continue
103 }
104
105 elems := make(map[string]interface{})
106 for it := bv.ElementIterator(); it.Next(); {
107 ek, ev := it.Element()
108 if !ev.IsKnown() {
109 elems[ek.AsString()] = UnknownVariableValue
110 continue
111 }
112 elems[ek.AsString()] = ConfigValueFromHCL2Block(ev, &blockS.Block)
113 }
114 ret[name] = elems
115 }
116 }
117
118 return ret
119 }
120
121 // ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic
122 // types library that HCL2 uses) to a value type that matches what would've
123 // been produced from the HCL-based interpolator for an equivalent structure.
124 //
125 // This function will transform a cty null value into a Go nil value, which
126 // isn't a possible outcome of the HCL/HIL-based decoder and so callers may
127 // need to detect and reject any null values.
128 func ConfigValueFromHCL2(v cty.Value) interface{} {
129 if !v.IsKnown() {
130 return UnknownVariableValue
131 }
132 if v.IsNull() {
133 return nil
134 }
135
136 switch v.Type() {
137 case cty.Bool:
138 return v.True() // like HCL.BOOL
139 case cty.String:
140 return v.AsString() // like HCL token.STRING or token.HEREDOC
141 case cty.Number:
142 // We can't match HCL _exactly_ here because it distinguishes between
143 // int and float values, but we'll get as close as we can by using
144 // an int if the number is exactly representable, and a float if not.
145 // The conversion to float will force precision to that of a float64,
146 // which is potentially losing information from the specific number
147 // given, but no worse than what HCL would've done in its own conversion
148 // to float.
149
150 f := v.AsBigFloat()
151 if i, acc := f.Int64(); acc == big.Exact {
152 // if we're on a 32-bit system and the number is too big for 32-bit
153 // int then we'll fall through here and use a float64.
154 const MaxInt = int(^uint(0) >> 1)
155 const MinInt = -MaxInt - 1
156 if i <= int64(MaxInt) && i >= int64(MinInt) {
157 return int(i) // Like HCL token.NUMBER
158 }
159 }
160
161 f64, _ := f.Float64()
162 return f64 // like HCL token.FLOAT
163 }
164
165 if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
166 l := make([]interface{}, 0, v.LengthInt())
167 it := v.ElementIterator()
168 for it.Next() {
169 _, ev := it.Element()
170 l = append(l, ConfigValueFromHCL2(ev))
171 }
172 return l
173 }
174
175 if v.Type().IsMapType() || v.Type().IsObjectType() {
176 l := make(map[string]interface{})
177 it := v.ElementIterator()
178 for it.Next() {
179 ek, ev := it.Element()
180 cv := ConfigValueFromHCL2(ev)
181 if cv != nil {
182 l[ek.AsString()] = cv
183 }
184 }
185 return l
186 }
187
188 // If we fall out here then we have some weird type that we haven't
189 // accounted for. This should never happen unless the caller is using
190 // capsule types, and we don't currently have any such types defined.
191 panic(fmt.Errorf("can't convert %#v to config value", v))
192 }
193
194 // HCL2ValueFromConfigValue is the opposite of configValueFromHCL2: it takes
195 // a value as would be returned from the old interpolator and turns it into
196 // a cty.Value so it can be used within, for example, an HCL2 EvalContext.
197 func HCL2ValueFromConfigValue(v interface{}) cty.Value {
198 if v == nil {
199 return cty.NullVal(cty.DynamicPseudoType)
200 }
201 if v == UnknownVariableValue {
202 return cty.DynamicVal
203 }
204
205 switch tv := v.(type) {
206 case bool:
207 return cty.BoolVal(tv)
208 case string:
209 return cty.StringVal(tv)
210 case int:
211 return cty.NumberIntVal(int64(tv))
212 case float64:
213 return cty.NumberFloatVal(tv)
214 case []interface{}:
215 vals := make([]cty.Value, len(tv))
216 for i, ev := range tv {
217 vals[i] = HCL2ValueFromConfigValue(ev)
218 }
219 return cty.TupleVal(vals)
220 case map[string]interface{}:
221 vals := map[string]cty.Value{}
222 for k, ev := range tv {
223 vals[k] = HCL2ValueFromConfigValue(ev)
224 }
225 return cty.ObjectVal(vals)
226 default:
227 // HCL/HIL should never generate anything that isn't caught by
228 // the above, so if we get here something has gone very wrong.
229 panic(fmt.Errorf("can't convert %#v to cty.Value", v))
230 }
231 }
232
233 func HILVariableFromHCL2Value(v cty.Value) ast.Variable {
234 if v.IsNull() {
235 // Caller should guarantee/check this before calling
236 panic("Null values cannot be represented in HIL")
237 }
238 if !v.IsKnown() {
239 return ast.Variable{
240 Type: ast.TypeUnknown,
241 Value: UnknownVariableValue,
242 }
243 }
244
245 switch v.Type() {
246 case cty.Bool:
247 return ast.Variable{
248 Type: ast.TypeBool,
249 Value: v.True(),
250 }
251 case cty.Number:
252 v := ConfigValueFromHCL2(v)
253 switch tv := v.(type) {
254 case int:
255 return ast.Variable{
256 Type: ast.TypeInt,
257 Value: tv,
258 }
259 case float64:
260 return ast.Variable{
261 Type: ast.TypeFloat,
262 Value: tv,
263 }
264 default:
265 // should never happen
266 panic("invalid return value for configValueFromHCL2")
267 }
268 case cty.String:
269 return ast.Variable{
270 Type: ast.TypeString,
271 Value: v.AsString(),
272 }
273 }
274
275 if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
276 l := make([]ast.Variable, 0, v.LengthInt())
277 it := v.ElementIterator()
278 for it.Next() {
279 _, ev := it.Element()
280 l = append(l, HILVariableFromHCL2Value(ev))
281 }
282 // If we were given a tuple then this could actually produce an invalid
283 // list with non-homogenous types, which we expect to be caught inside
284 // HIL just like a user-supplied non-homogenous list would be.
285 return ast.Variable{
286 Type: ast.TypeList,
287 Value: l,
288 }
289 }
290
291 if v.Type().IsMapType() || v.Type().IsObjectType() {
292 l := make(map[string]ast.Variable)
293 it := v.ElementIterator()
294 for it.Next() {
295 ek, ev := it.Element()
296 l[ek.AsString()] = HILVariableFromHCL2Value(ev)
297 }
298 // If we were given an object then this could actually produce an invalid
299 // map with non-homogenous types, which we expect to be caught inside
300 // HIL just like a user-supplied non-homogenous map would be.
301 return ast.Variable{
302 Type: ast.TypeMap,
303 Value: l,
304 }
305 }
306
307 // If we fall out here then we have some weird type that we haven't
308 // accounted for. This should never happen unless the caller is using
309 // capsule types, and we don't currently have any such types defined.
310 panic(fmt.Errorf("can't convert %#v to HIL variable", v))
311 }
312
313 func HCL2ValueFromHILVariable(v ast.Variable) cty.Value {
314 switch v.Type {
315 case ast.TypeList:
316 vals := make([]cty.Value, len(v.Value.([]ast.Variable)))
317 for i, ev := range v.Value.([]ast.Variable) {
318 vals[i] = HCL2ValueFromHILVariable(ev)
319 }
320 return cty.TupleVal(vals)
321 case ast.TypeMap:
322 vals := make(map[string]cty.Value, len(v.Value.(map[string]ast.Variable)))
323 for k, ev := range v.Value.(map[string]ast.Variable) {
324 vals[k] = HCL2ValueFromHILVariable(ev)
325 }
326 return cty.ObjectVal(vals)
327 default:
328 return HCL2ValueFromConfigValue(v.Value)
329 }
330 }
331
332 func HCL2TypeForHILType(hilType ast.Type) cty.Type {
333 switch hilType {
334 case ast.TypeAny:
335 return cty.DynamicPseudoType
336 case ast.TypeUnknown:
337 return cty.DynamicPseudoType
338 case ast.TypeBool:
339 return cty.Bool
340 case ast.TypeInt:
341 return cty.Number
342 case ast.TypeFloat:
343 return cty.Number
344 case ast.TypeString:
345 return cty.String
346 case ast.TypeList:
347 return cty.List(cty.DynamicPseudoType)
348 case ast.TypeMap:
349 return cty.Map(cty.DynamicPseudoType)
350 default:
351 return cty.NilType // equilvalent to ast.TypeInvalid
352 }
353 }