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/plans/objchange/normalize_obj.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/plans/objchange/normalize_obj.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go b/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go new file mode 100644 index 0000000..c23f44d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go | |||
@@ -0,0 +1,132 @@ | |||
1 | package objchange | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/terraform/configs/configschema" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | // NormalizeObjectFromLegacySDK takes an object that may have been generated | ||
9 | // by the legacy Terraform SDK (i.e. returned from a provider with the | ||
10 | // LegacyTypeSystem opt-out set) and does its best to normalize it for the | ||
11 | // assumptions we would normally enforce if the provider had not opted out. | ||
12 | // | ||
13 | // In particular, this function guarantees that a value representing a nested | ||
14 | // block will never itself be unknown or null, instead representing that as | ||
15 | // a non-null value that may contain null/unknown values. | ||
16 | // | ||
17 | // The input value must still conform to the implied type of the given schema, | ||
18 | // or else this function may produce garbage results or panic. This is usually | ||
19 | // okay because type consistency is enforced when deserializing the value | ||
20 | // returned from the provider over the RPC wire protocol anyway. | ||
21 | func NormalizeObjectFromLegacySDK(val cty.Value, schema *configschema.Block) cty.Value { | ||
22 | if val == cty.NilVal || val.IsNull() { | ||
23 | // This should never happen in reasonable use, but we'll allow it | ||
24 | // and normalize to a null of the expected type rather than panicking | ||
25 | // below. | ||
26 | return cty.NullVal(schema.ImpliedType()) | ||
27 | } | ||
28 | |||
29 | vals := make(map[string]cty.Value) | ||
30 | for name := range schema.Attributes { | ||
31 | // No normalization for attributes, since them being type-conformant | ||
32 | // is all that we require. | ||
33 | vals[name] = val.GetAttr(name) | ||
34 | } | ||
35 | for name, blockS := range schema.BlockTypes { | ||
36 | lv := val.GetAttr(name) | ||
37 | |||
38 | // Legacy SDK never generates dynamically-typed attributes and so our | ||
39 | // normalization code doesn't deal with them, but we need to make sure | ||
40 | // we still pass them through properly so that we don't interfere with | ||
41 | // objects generated by other SDKs. | ||
42 | if ty := blockS.Block.ImpliedType(); ty.HasDynamicTypes() { | ||
43 | vals[name] = lv | ||
44 | continue | ||
45 | } | ||
46 | |||
47 | switch blockS.Nesting { | ||
48 | case configschema.NestingSingle, configschema.NestingGroup: | ||
49 | if lv.IsKnown() { | ||
50 | if lv.IsNull() && blockS.Nesting == configschema.NestingGroup { | ||
51 | vals[name] = blockS.EmptyValue() | ||
52 | } else { | ||
53 | vals[name] = NormalizeObjectFromLegacySDK(lv, &blockS.Block) | ||
54 | } | ||
55 | } else { | ||
56 | vals[name] = unknownBlockStub(&blockS.Block) | ||
57 | } | ||
58 | case configschema.NestingList: | ||
59 | switch { | ||
60 | case !lv.IsKnown(): | ||
61 | vals[name] = cty.ListVal([]cty.Value{unknownBlockStub(&blockS.Block)}) | ||
62 | case lv.IsNull() || lv.LengthInt() == 0: | ||
63 | vals[name] = cty.ListValEmpty(blockS.Block.ImpliedType()) | ||
64 | default: | ||
65 | subVals := make([]cty.Value, 0, lv.LengthInt()) | ||
66 | for it := lv.ElementIterator(); it.Next(); { | ||
67 | _, subVal := it.Element() | ||
68 | subVals = append(subVals, NormalizeObjectFromLegacySDK(subVal, &blockS.Block)) | ||
69 | } | ||
70 | vals[name] = cty.ListVal(subVals) | ||
71 | } | ||
72 | case configschema.NestingSet: | ||
73 | switch { | ||
74 | case !lv.IsKnown(): | ||
75 | vals[name] = cty.SetVal([]cty.Value{unknownBlockStub(&blockS.Block)}) | ||
76 | case lv.IsNull() || lv.LengthInt() == 0: | ||
77 | vals[name] = cty.SetValEmpty(blockS.Block.ImpliedType()) | ||
78 | default: | ||
79 | subVals := make([]cty.Value, 0, lv.LengthInt()) | ||
80 | for it := lv.ElementIterator(); it.Next(); { | ||
81 | _, subVal := it.Element() | ||
82 | subVals = append(subVals, NormalizeObjectFromLegacySDK(subVal, &blockS.Block)) | ||
83 | } | ||
84 | vals[name] = cty.SetVal(subVals) | ||
85 | } | ||
86 | default: | ||
87 | // The legacy SDK doesn't support NestingMap, so we just assume | ||
88 | // maps are always okay. (If not, we would've detected and returned | ||
89 | // an error to the user before we got here.) | ||
90 | vals[name] = lv | ||
91 | } | ||
92 | } | ||
93 | return cty.ObjectVal(vals) | ||
94 | } | ||
95 | |||
96 | // unknownBlockStub constructs an object value that approximates an unknown | ||
97 | // block by producing a known block object with all of its leaf attribute | ||
98 | // values set to unknown. | ||
99 | // | ||
100 | // Blocks themselves cannot be unknown, so if the legacy SDK tries to return | ||
101 | // such a thing, we'll use this result instead. This convention mimics how | ||
102 | // the dynamic block feature deals with being asked to iterate over an unknown | ||
103 | // value, because our value-checking functions already accept this convention | ||
104 | // as a special case. | ||
105 | func unknownBlockStub(schema *configschema.Block) cty.Value { | ||
106 | vals := make(map[string]cty.Value) | ||
107 | for name, attrS := range schema.Attributes { | ||
108 | vals[name] = cty.UnknownVal(attrS.Type) | ||
109 | } | ||
110 | for name, blockS := range schema.BlockTypes { | ||
111 | switch blockS.Nesting { | ||
112 | case configschema.NestingSingle, configschema.NestingGroup: | ||
113 | vals[name] = unknownBlockStub(&blockS.Block) | ||
114 | case configschema.NestingList: | ||
115 | // In principle we may be expected to produce a tuple value here, | ||
116 | // if there are any dynamically-typed attributes in our nested block, | ||
117 | // but the legacy SDK doesn't support that, so we just assume it'll | ||
118 | // never be necessary to normalize those. (Incorrect usage in any | ||
119 | // other SDK would be caught and returned as an error before we | ||
120 | // get here.) | ||
121 | vals[name] = cty.ListVal([]cty.Value{unknownBlockStub(&blockS.Block)}) | ||
122 | case configschema.NestingSet: | ||
123 | vals[name] = cty.SetVal([]cty.Value{unknownBlockStub(&blockS.Block)}) | ||
124 | case configschema.NestingMap: | ||
125 | // A nesting map can never be unknown since we then wouldn't know | ||
126 | // what the keys are. (Legacy SDK doesn't support NestingMap anyway, | ||
127 | // so this should never arise.) | ||
128 | vals[name] = cty.MapValEmpty(blockS.Block.ImpliedType()) | ||
129 | } | ||
130 | } | ||
131 | return cty.ObjectVal(vals) | ||
132 | } | ||