diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go new file mode 100644 index 0000000..bb4228d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/flatmap.go | |||
@@ -0,0 +1,424 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strconv" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/zclconf/go-cty/cty/convert" | ||
9 | |||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | ) | ||
12 | |||
13 | // FlatmapValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic | ||
14 | // types library that HCL2 uses) to a map compatible with what would be | ||
15 | // produced by the "flatmap" package. | ||
16 | // | ||
17 | // The type of the given value informs the structure of the resulting map. | ||
18 | // The value must be of an object type or this function will panic. | ||
19 | // | ||
20 | // Flatmap values can only represent maps when they are of primitive types, | ||
21 | // so the given value must not have any maps of complex types or the result | ||
22 | // is undefined. | ||
23 | func FlatmapValueFromHCL2(v cty.Value) map[string]string { | ||
24 | if v.IsNull() { | ||
25 | return nil | ||
26 | } | ||
27 | |||
28 | if !v.Type().IsObjectType() { | ||
29 | panic(fmt.Sprintf("HCL2ValueFromFlatmap called on %#v", v.Type())) | ||
30 | } | ||
31 | |||
32 | m := make(map[string]string) | ||
33 | flatmapValueFromHCL2Map(m, "", v) | ||
34 | return m | ||
35 | } | ||
36 | |||
37 | func flatmapValueFromHCL2Value(m map[string]string, key string, val cty.Value) { | ||
38 | ty := val.Type() | ||
39 | switch { | ||
40 | case ty.IsPrimitiveType() || ty == cty.DynamicPseudoType: | ||
41 | flatmapValueFromHCL2Primitive(m, key, val) | ||
42 | case ty.IsObjectType() || ty.IsMapType(): | ||
43 | flatmapValueFromHCL2Map(m, key+".", val) | ||
44 | case ty.IsTupleType() || ty.IsListType() || ty.IsSetType(): | ||
45 | flatmapValueFromHCL2Seq(m, key+".", val) | ||
46 | default: | ||
47 | panic(fmt.Sprintf("cannot encode %s to flatmap", ty.FriendlyName())) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | func flatmapValueFromHCL2Primitive(m map[string]string, key string, val cty.Value) { | ||
52 | if !val.IsKnown() { | ||
53 | m[key] = UnknownVariableValue | ||
54 | return | ||
55 | } | ||
56 | if val.IsNull() { | ||
57 | // Omit entirely | ||
58 | return | ||
59 | } | ||
60 | |||
61 | var err error | ||
62 | val, err = convert.Convert(val, cty.String) | ||
63 | if err != nil { | ||
64 | // Should not be possible, since all primitive types can convert to string. | ||
65 | panic(fmt.Sprintf("invalid primitive encoding to flatmap: %s", err)) | ||
66 | } | ||
67 | m[key] = val.AsString() | ||
68 | } | ||
69 | |||
70 | func flatmapValueFromHCL2Map(m map[string]string, prefix string, val cty.Value) { | ||
71 | if val.IsNull() { | ||
72 | // Omit entirely | ||
73 | return | ||
74 | } | ||
75 | if !val.IsKnown() { | ||
76 | switch { | ||
77 | case val.Type().IsObjectType(): | ||
78 | // Whole objects can't be unknown in flatmap, so instead we'll | ||
79 | // just write all of the attribute values out as unknown. | ||
80 | for name, aty := range val.Type().AttributeTypes() { | ||
81 | flatmapValueFromHCL2Value(m, prefix+name, cty.UnknownVal(aty)) | ||
82 | } | ||
83 | default: | ||
84 | m[prefix+"%"] = UnknownVariableValue | ||
85 | } | ||
86 | return | ||
87 | } | ||
88 | |||
89 | len := 0 | ||
90 | for it := val.ElementIterator(); it.Next(); { | ||
91 | ak, av := it.Element() | ||
92 | name := ak.AsString() | ||
93 | flatmapValueFromHCL2Value(m, prefix+name, av) | ||
94 | len++ | ||
95 | } | ||
96 | if !val.Type().IsObjectType() { // objects don't have an explicit count included, since their attribute count is fixed | ||
97 | m[prefix+"%"] = strconv.Itoa(len) | ||
98 | } | ||
99 | } | ||
100 | |||
101 | func flatmapValueFromHCL2Seq(m map[string]string, prefix string, val cty.Value) { | ||
102 | if val.IsNull() { | ||
103 | // Omit entirely | ||
104 | return | ||
105 | } | ||
106 | if !val.IsKnown() { | ||
107 | m[prefix+"#"] = UnknownVariableValue | ||
108 | return | ||
109 | } | ||
110 | |||
111 | // For sets this won't actually generate exactly what helper/schema would've | ||
112 | // generated, because we don't have access to the set key function it | ||
113 | // would've used. However, in practice it doesn't actually matter what the | ||
114 | // keys are as long as they are unique, so we'll just generate sequential | ||
115 | // indexes for them as if it were a list. | ||
116 | // | ||
117 | // An important implication of this, however, is that the set ordering will | ||
118 | // not be consistent across mutations and so different keys may be assigned | ||
119 | // to the same value when round-tripping. Since this shim is intended to | ||
120 | // be short-lived and not used for round-tripping, we accept this. | ||
121 | i := 0 | ||
122 | for it := val.ElementIterator(); it.Next(); { | ||
123 | _, av := it.Element() | ||
124 | key := prefix + strconv.Itoa(i) | ||
125 | flatmapValueFromHCL2Value(m, key, av) | ||
126 | i++ | ||
127 | } | ||
128 | m[prefix+"#"] = strconv.Itoa(i) | ||
129 | } | ||
130 | |||
131 | // HCL2ValueFromFlatmap converts a map compatible with what would be produced | ||
132 | // by the "flatmap" package to a HCL2 (really, the cty dynamic types library | ||
133 | // that HCL2 uses) object type. | ||
134 | // | ||
135 | // The intended result type must be provided in order to guide how the | ||
136 | // map contents are decoded. This must be an object type or this function | ||
137 | // will panic. | ||
138 | // | ||
139 | // Flatmap values can only represent maps when they are of primitive types, | ||
140 | // so the given type must not have any maps of complex types or the result | ||
141 | // is undefined. | ||
142 | // | ||
143 | // The result may contain null values if the given map does not contain keys | ||
144 | // for all of the different key paths implied by the given type. | ||
145 | func HCL2ValueFromFlatmap(m map[string]string, ty cty.Type) (cty.Value, error) { | ||
146 | if m == nil { | ||
147 | return cty.NullVal(ty), nil | ||
148 | } | ||
149 | if !ty.IsObjectType() { | ||
150 | panic(fmt.Sprintf("HCL2ValueFromFlatmap called on %#v", ty)) | ||
151 | } | ||
152 | |||
153 | return hcl2ValueFromFlatmapObject(m, "", ty.AttributeTypes()) | ||
154 | } | ||
155 | |||
156 | func hcl2ValueFromFlatmapValue(m map[string]string, key string, ty cty.Type) (cty.Value, error) { | ||
157 | var val cty.Value | ||
158 | var err error | ||
159 | switch { | ||
160 | case ty.IsPrimitiveType(): | ||
161 | val, err = hcl2ValueFromFlatmapPrimitive(m, key, ty) | ||
162 | case ty.IsObjectType(): | ||
163 | val, err = hcl2ValueFromFlatmapObject(m, key+".", ty.AttributeTypes()) | ||
164 | case ty.IsTupleType(): | ||
165 | val, err = hcl2ValueFromFlatmapTuple(m, key+".", ty.TupleElementTypes()) | ||
166 | case ty.IsMapType(): | ||
167 | val, err = hcl2ValueFromFlatmapMap(m, key+".", ty) | ||
168 | case ty.IsListType(): | ||
169 | val, err = hcl2ValueFromFlatmapList(m, key+".", ty) | ||
170 | case ty.IsSetType(): | ||
171 | val, err = hcl2ValueFromFlatmapSet(m, key+".", ty) | ||
172 | default: | ||
173 | err = fmt.Errorf("cannot decode %s from flatmap", ty.FriendlyName()) | ||
174 | } | ||
175 | |||
176 | if err != nil { | ||
177 | return cty.DynamicVal, err | ||
178 | } | ||
179 | return val, nil | ||
180 | } | ||
181 | |||
182 | func hcl2ValueFromFlatmapPrimitive(m map[string]string, key string, ty cty.Type) (cty.Value, error) { | ||
183 | rawVal, exists := m[key] | ||
184 | if !exists { | ||
185 | return cty.NullVal(ty), nil | ||
186 | } | ||
187 | if rawVal == UnknownVariableValue { | ||
188 | return cty.UnknownVal(ty), nil | ||
189 | } | ||
190 | |||
191 | var err error | ||
192 | val := cty.StringVal(rawVal) | ||
193 | val, err = convert.Convert(val, ty) | ||
194 | if err != nil { | ||
195 | // This should never happen for _valid_ input, but flatmap data might | ||
196 | // be tampered with by the user and become invalid. | ||
197 | return cty.DynamicVal, fmt.Errorf("invalid value for %q in state: %s", key, err) | ||
198 | } | ||
199 | |||
200 | return val, nil | ||
201 | } | ||
202 | |||
203 | func hcl2ValueFromFlatmapObject(m map[string]string, prefix string, atys map[string]cty.Type) (cty.Value, error) { | ||
204 | vals := make(map[string]cty.Value) | ||
205 | for name, aty := range atys { | ||
206 | val, err := hcl2ValueFromFlatmapValue(m, prefix+name, aty) | ||
207 | if err != nil { | ||
208 | return cty.DynamicVal, err | ||
209 | } | ||
210 | vals[name] = val | ||
211 | } | ||
212 | return cty.ObjectVal(vals), nil | ||
213 | } | ||
214 | |||
215 | func hcl2ValueFromFlatmapTuple(m map[string]string, prefix string, etys []cty.Type) (cty.Value, error) { | ||
216 | var vals []cty.Value | ||
217 | |||
218 | // if the container is unknown, there is no count string | ||
219 | listName := strings.TrimRight(prefix, ".") | ||
220 | if m[listName] == UnknownVariableValue { | ||
221 | return cty.UnknownVal(cty.Tuple(etys)), nil | ||
222 | } | ||
223 | |||
224 | countStr, exists := m[prefix+"#"] | ||
225 | if !exists { | ||
226 | return cty.NullVal(cty.Tuple(etys)), nil | ||
227 | } | ||
228 | if countStr == UnknownVariableValue { | ||
229 | return cty.UnknownVal(cty.Tuple(etys)), nil | ||
230 | } | ||
231 | |||
232 | count, err := strconv.Atoi(countStr) | ||
233 | if err != nil { | ||
234 | return cty.DynamicVal, fmt.Errorf("invalid count value for %q in state: %s", prefix, err) | ||
235 | } | ||
236 | if count != len(etys) { | ||
237 | return cty.DynamicVal, fmt.Errorf("wrong number of values for %q in state: got %d, but need %d", prefix, count, len(etys)) | ||
238 | } | ||
239 | |||
240 | vals = make([]cty.Value, len(etys)) | ||
241 | for i, ety := range etys { | ||
242 | key := prefix + strconv.Itoa(i) | ||
243 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
244 | if err != nil { | ||
245 | return cty.DynamicVal, err | ||
246 | } | ||
247 | vals[i] = val | ||
248 | } | ||
249 | return cty.TupleVal(vals), nil | ||
250 | } | ||
251 | |||
252 | func hcl2ValueFromFlatmapMap(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
253 | vals := make(map[string]cty.Value) | ||
254 | ety := ty.ElementType() | ||
255 | |||
256 | // if the container is unknown, there is no count string | ||
257 | listName := strings.TrimRight(prefix, ".") | ||
258 | if m[listName] == UnknownVariableValue { | ||
259 | return cty.UnknownVal(ty), nil | ||
260 | } | ||
261 | |||
262 | // We actually don't really care about the "count" of a map for our | ||
263 | // purposes here, but we do need to check if it _exists_ in order to | ||
264 | // recognize the difference between null (not set at all) and empty. | ||
265 | if strCount, exists := m[prefix+"%"]; !exists { | ||
266 | return cty.NullVal(ty), nil | ||
267 | } else if strCount == UnknownVariableValue { | ||
268 | return cty.UnknownVal(ty), nil | ||
269 | } | ||
270 | |||
271 | for fullKey := range m { | ||
272 | if !strings.HasPrefix(fullKey, prefix) { | ||
273 | continue | ||
274 | } | ||
275 | |||
276 | // The flatmap format doesn't allow us to distinguish between keys | ||
277 | // that contain periods and nested objects, so by convention a | ||
278 | // map is only ever of primitive type in flatmap, and we just assume | ||
279 | // that the remainder of the raw key (dots and all) is the key we | ||
280 | // want in the result value. | ||
281 | key := fullKey[len(prefix):] | ||
282 | if key == "%" { | ||
283 | // Ignore the "count" key | ||
284 | continue | ||
285 | } | ||
286 | |||
287 | val, err := hcl2ValueFromFlatmapValue(m, fullKey, ety) | ||
288 | if err != nil { | ||
289 | return cty.DynamicVal, err | ||
290 | } | ||
291 | vals[key] = val | ||
292 | } | ||
293 | |||
294 | if len(vals) == 0 { | ||
295 | return cty.MapValEmpty(ety), nil | ||
296 | } | ||
297 | return cty.MapVal(vals), nil | ||
298 | } | ||
299 | |||
300 | func hcl2ValueFromFlatmapList(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
301 | var vals []cty.Value | ||
302 | |||
303 | // if the container is unknown, there is no count string | ||
304 | listName := strings.TrimRight(prefix, ".") | ||
305 | if m[listName] == UnknownVariableValue { | ||
306 | return cty.UnknownVal(ty), nil | ||
307 | } | ||
308 | |||
309 | countStr, exists := m[prefix+"#"] | ||
310 | if !exists { | ||
311 | return cty.NullVal(ty), nil | ||
312 | } | ||
313 | if countStr == UnknownVariableValue { | ||
314 | return cty.UnknownVal(ty), nil | ||
315 | } | ||
316 | |||
317 | count, err := strconv.Atoi(countStr) | ||
318 | if err != nil { | ||
319 | return cty.DynamicVal, fmt.Errorf("invalid count value for %q in state: %s", prefix, err) | ||
320 | } | ||
321 | |||
322 | ety := ty.ElementType() | ||
323 | if count == 0 { | ||
324 | return cty.ListValEmpty(ety), nil | ||
325 | } | ||
326 | |||
327 | vals = make([]cty.Value, count) | ||
328 | for i := 0; i < count; i++ { | ||
329 | key := prefix + strconv.Itoa(i) | ||
330 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
331 | if err != nil { | ||
332 | return cty.DynamicVal, err | ||
333 | } | ||
334 | vals[i] = val | ||
335 | } | ||
336 | |||
337 | return cty.ListVal(vals), nil | ||
338 | } | ||
339 | |||
340 | func hcl2ValueFromFlatmapSet(m map[string]string, prefix string, ty cty.Type) (cty.Value, error) { | ||
341 | var vals []cty.Value | ||
342 | ety := ty.ElementType() | ||
343 | |||
344 | // if the container is unknown, there is no count string | ||
345 | listName := strings.TrimRight(prefix, ".") | ||
346 | if m[listName] == UnknownVariableValue { | ||
347 | return cty.UnknownVal(ty), nil | ||
348 | } | ||
349 | |||
350 | strCount, exists := m[prefix+"#"] | ||
351 | if !exists { | ||
352 | return cty.NullVal(ty), nil | ||
353 | } else if strCount == UnknownVariableValue { | ||
354 | return cty.UnknownVal(ty), nil | ||
355 | } | ||
356 | |||
357 | // Keep track of keys we've seen, se we don't add the same set value | ||
358 | // multiple times. The cty.Set will normally de-duplicate values, but we may | ||
359 | // have unknown values that would not show as equivalent. | ||
360 | seen := map[string]bool{} | ||
361 | |||
362 | for fullKey := range m { | ||
363 | if !strings.HasPrefix(fullKey, prefix) { | ||
364 | continue | ||
365 | } | ||
366 | subKey := fullKey[len(prefix):] | ||
367 | if subKey == "#" { | ||
368 | // Ignore the "count" key | ||
369 | continue | ||
370 | } | ||
371 | key := fullKey | ||
372 | if dot := strings.IndexByte(subKey, '.'); dot != -1 { | ||
373 | key = fullKey[:dot+len(prefix)] | ||
374 | } | ||
375 | |||
376 | if seen[key] { | ||
377 | continue | ||
378 | } | ||
379 | |||
380 | seen[key] = true | ||
381 | |||
382 | // The flatmap format doesn't allow us to distinguish between keys | ||
383 | // that contain periods and nested objects, so by convention a | ||
384 | // map is only ever of primitive type in flatmap, and we just assume | ||
385 | // that the remainder of the raw key (dots and all) is the key we | ||
386 | // want in the result value. | ||
387 | |||
388 | val, err := hcl2ValueFromFlatmapValue(m, key, ety) | ||
389 | if err != nil { | ||
390 | return cty.DynamicVal, err | ||
391 | } | ||
392 | vals = append(vals, val) | ||
393 | } | ||
394 | |||
395 | if len(vals) == 0 && strCount == "1" { | ||
396 | // An empty set wouldn't be represented in the flatmap, so this must be | ||
397 | // a single empty object since the count is actually 1. | ||
398 | // Add an appropriately typed null value to the set. | ||
399 | var val cty.Value | ||
400 | switch { | ||
401 | case ety.IsMapType(): | ||
402 | val = cty.MapValEmpty(ety) | ||
403 | case ety.IsListType(): | ||
404 | val = cty.ListValEmpty(ety) | ||
405 | case ety.IsSetType(): | ||
406 | val = cty.SetValEmpty(ety) | ||
407 | case ety.IsObjectType(): | ||
408 | // TODO: cty.ObjectValEmpty | ||
409 | objectMap := map[string]cty.Value{} | ||
410 | for attr, ty := range ety.AttributeTypes() { | ||
411 | objectMap[attr] = cty.NullVal(ty) | ||
412 | } | ||
413 | val = cty.ObjectVal(objectMap) | ||
414 | default: | ||
415 | val = cty.NullVal(ety) | ||
416 | } | ||
417 | vals = append(vals, val) | ||
418 | |||
419 | } else if len(vals) == 0 { | ||
420 | return cty.SetValEmpty(ety), nil | ||
421 | } | ||
422 | |||
423 | return cty.SetVal(vals), nil | ||
424 | } | ||