diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/terraform/config/interpolate_walk.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/interpolate_walk.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/interpolate_walk.go | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go new file mode 100644 index 0000000..ead3d10 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go | |||
@@ -0,0 +1,283 @@ | |||
1 | package config | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "reflect" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/hashicorp/hil" | ||
9 | "github.com/hashicorp/hil/ast" | ||
10 | "github.com/mitchellh/reflectwalk" | ||
11 | ) | ||
12 | |||
13 | // interpolationWalker implements interfaces for the reflectwalk package | ||
14 | // (github.com/mitchellh/reflectwalk) that can be used to automatically | ||
15 | // execute a callback for an interpolation. | ||
16 | type interpolationWalker struct { | ||
17 | // F is the function to call for every interpolation. It can be nil. | ||
18 | // | ||
19 | // If Replace is true, then the return value of F will be used to | ||
20 | // replace the interpolation. | ||
21 | F interpolationWalkerFunc | ||
22 | Replace bool | ||
23 | |||
24 | // ContextF is an advanced version of F that also receives the | ||
25 | // location of where it is in the structure. This lets you do | ||
26 | // context-aware validation. | ||
27 | ContextF interpolationWalkerContextFunc | ||
28 | |||
29 | key []string | ||
30 | lastValue reflect.Value | ||
31 | loc reflectwalk.Location | ||
32 | cs []reflect.Value | ||
33 | csKey []reflect.Value | ||
34 | csData interface{} | ||
35 | sliceIndex []int | ||
36 | unknownKeys []string | ||
37 | } | ||
38 | |||
39 | // interpolationWalkerFunc is the callback called by interpolationWalk. | ||
40 | // It is called with any interpolation found. It should return a value | ||
41 | // to replace the interpolation with, along with any errors. | ||
42 | // | ||
43 | // If Replace is set to false in interpolationWalker, then the replace | ||
44 | // value can be anything as it will have no effect. | ||
45 | type interpolationWalkerFunc func(ast.Node) (interface{}, error) | ||
46 | |||
47 | // interpolationWalkerContextFunc is called by interpolationWalk if | ||
48 | // ContextF is set. This receives both the interpolation and the location | ||
49 | // where the interpolation is. | ||
50 | // | ||
51 | // This callback can be used to validate the location of the interpolation | ||
52 | // within the configuration. | ||
53 | type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node) | ||
54 | |||
55 | func (w *interpolationWalker) Enter(loc reflectwalk.Location) error { | ||
56 | w.loc = loc | ||
57 | return nil | ||
58 | } | ||
59 | |||
60 | func (w *interpolationWalker) Exit(loc reflectwalk.Location) error { | ||
61 | w.loc = reflectwalk.None | ||
62 | |||
63 | switch loc { | ||
64 | case reflectwalk.Map: | ||
65 | w.cs = w.cs[:len(w.cs)-1] | ||
66 | case reflectwalk.MapValue: | ||
67 | w.key = w.key[:len(w.key)-1] | ||
68 | w.csKey = w.csKey[:len(w.csKey)-1] | ||
69 | case reflectwalk.Slice: | ||
70 | // Split any values that need to be split | ||
71 | w.splitSlice() | ||
72 | w.cs = w.cs[:len(w.cs)-1] | ||
73 | case reflectwalk.SliceElem: | ||
74 | w.csKey = w.csKey[:len(w.csKey)-1] | ||
75 | w.sliceIndex = w.sliceIndex[:len(w.sliceIndex)-1] | ||
76 | } | ||
77 | |||
78 | return nil | ||
79 | } | ||
80 | |||
81 | func (w *interpolationWalker) Map(m reflect.Value) error { | ||
82 | w.cs = append(w.cs, m) | ||
83 | return nil | ||
84 | } | ||
85 | |||
86 | func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error { | ||
87 | w.csData = k | ||
88 | w.csKey = append(w.csKey, k) | ||
89 | |||
90 | if l := len(w.sliceIndex); l > 0 { | ||
91 | w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex[l-1], k.String())) | ||
92 | } else { | ||
93 | w.key = append(w.key, k.String()) | ||
94 | } | ||
95 | |||
96 | w.lastValue = v | ||
97 | return nil | ||
98 | } | ||
99 | |||
100 | func (w *interpolationWalker) Slice(s reflect.Value) error { | ||
101 | w.cs = append(w.cs, s) | ||
102 | return nil | ||
103 | } | ||
104 | |||
105 | func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error { | ||
106 | w.csKey = append(w.csKey, reflect.ValueOf(i)) | ||
107 | w.sliceIndex = append(w.sliceIndex, i) | ||
108 | return nil | ||
109 | } | ||
110 | |||
111 | func (w *interpolationWalker) Primitive(v reflect.Value) error { | ||
112 | setV := v | ||
113 | |||
114 | // We only care about strings | ||
115 | if v.Kind() == reflect.Interface { | ||
116 | setV = v | ||
117 | v = v.Elem() | ||
118 | } | ||
119 | if v.Kind() != reflect.String { | ||
120 | return nil | ||
121 | } | ||
122 | |||
123 | astRoot, err := hil.Parse(v.String()) | ||
124 | if err != nil { | ||
125 | return err | ||
126 | } | ||
127 | |||
128 | // If the AST we got is just a literal string value with the same | ||
129 | // value then we ignore it. We have to check if its the same value | ||
130 | // because it is possible to input a string, get out a string, and | ||
131 | // have it be different. For example: "foo-$${bar}" turns into | ||
132 | // "foo-${bar}" | ||
133 | if n, ok := astRoot.(*ast.LiteralNode); ok { | ||
134 | if s, ok := n.Value.(string); ok && s == v.String() { | ||
135 | return nil | ||
136 | } | ||
137 | } | ||
138 | |||
139 | if w.ContextF != nil { | ||
140 | w.ContextF(w.loc, astRoot) | ||
141 | } | ||
142 | |||
143 | if w.F == nil { | ||
144 | return nil | ||
145 | } | ||
146 | |||
147 | replaceVal, err := w.F(astRoot) | ||
148 | if err != nil { | ||
149 | return fmt.Errorf( | ||
150 | "%s in:\n\n%s", | ||
151 | err, v.String()) | ||
152 | } | ||
153 | |||
154 | if w.Replace { | ||
155 | // We need to determine if we need to remove this element | ||
156 | // if the result contains any "UnknownVariableValue" which is | ||
157 | // set if it is computed. This behavior is different if we're | ||
158 | // splitting (in a SliceElem) or not. | ||
159 | remove := false | ||
160 | if w.loc == reflectwalk.SliceElem { | ||
161 | switch typedReplaceVal := replaceVal.(type) { | ||
162 | case string: | ||
163 | if typedReplaceVal == UnknownVariableValue { | ||
164 | remove = true | ||
165 | } | ||
166 | case []interface{}: | ||
167 | if hasUnknownValue(typedReplaceVal) { | ||
168 | remove = true | ||
169 | } | ||
170 | } | ||
171 | } else if replaceVal == UnknownVariableValue { | ||
172 | remove = true | ||
173 | } | ||
174 | |||
175 | if remove { | ||
176 | w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) | ||
177 | } | ||
178 | |||
179 | resultVal := reflect.ValueOf(replaceVal) | ||
180 | switch w.loc { | ||
181 | case reflectwalk.MapKey: | ||
182 | m := w.cs[len(w.cs)-1] | ||
183 | |||
184 | // Delete the old value | ||
185 | var zero reflect.Value | ||
186 | m.SetMapIndex(w.csData.(reflect.Value), zero) | ||
187 | |||
188 | // Set the new key with the existing value | ||
189 | m.SetMapIndex(resultVal, w.lastValue) | ||
190 | |||
191 | // Set the key to be the new key | ||
192 | w.csData = resultVal | ||
193 | case reflectwalk.MapValue: | ||
194 | // If we're in a map, then the only way to set a map value is | ||
195 | // to set it directly. | ||
196 | m := w.cs[len(w.cs)-1] | ||
197 | mk := w.csData.(reflect.Value) | ||
198 | m.SetMapIndex(mk, resultVal) | ||
199 | default: | ||
200 | // Otherwise, we should be addressable | ||
201 | setV.Set(resultVal) | ||
202 | } | ||
203 | } | ||
204 | |||
205 | return nil | ||
206 | } | ||
207 | |||
208 | func (w *interpolationWalker) replaceCurrent(v reflect.Value) { | ||
209 | // if we don't have at least 2 values, we're not going to find a map, but | ||
210 | // we could panic. | ||
211 | if len(w.cs) < 2 { | ||
212 | return | ||
213 | } | ||
214 | |||
215 | c := w.cs[len(w.cs)-2] | ||
216 | switch c.Kind() { | ||
217 | case reflect.Map: | ||
218 | // Get the key and delete it | ||
219 | k := w.csKey[len(w.csKey)-1] | ||
220 | c.SetMapIndex(k, v) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | func hasUnknownValue(variable []interface{}) bool { | ||
225 | for _, value := range variable { | ||
226 | if strVal, ok := value.(string); ok { | ||
227 | if strVal == UnknownVariableValue { | ||
228 | return true | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | return false | ||
233 | } | ||
234 | |||
235 | func (w *interpolationWalker) splitSlice() { | ||
236 | raw := w.cs[len(w.cs)-1] | ||
237 | |||
238 | var s []interface{} | ||
239 | switch v := raw.Interface().(type) { | ||
240 | case []interface{}: | ||
241 | s = v | ||
242 | case []map[string]interface{}: | ||
243 | return | ||
244 | } | ||
245 | |||
246 | split := false | ||
247 | for _, val := range s { | ||
248 | if varVal, ok := val.(ast.Variable); ok && varVal.Type == ast.TypeList { | ||
249 | split = true | ||
250 | } | ||
251 | if _, ok := val.([]interface{}); ok { | ||
252 | split = true | ||
253 | } | ||
254 | } | ||
255 | |||
256 | if !split { | ||
257 | return | ||
258 | } | ||
259 | |||
260 | result := make([]interface{}, 0) | ||
261 | for _, v := range s { | ||
262 | switch val := v.(type) { | ||
263 | case ast.Variable: | ||
264 | switch val.Type { | ||
265 | case ast.TypeList: | ||
266 | elements := val.Value.([]ast.Variable) | ||
267 | for _, element := range elements { | ||
268 | result = append(result, element.Value) | ||
269 | } | ||
270 | default: | ||
271 | result = append(result, val.Value) | ||
272 | } | ||
273 | case []interface{}: | ||
274 | for _, element := range val { | ||
275 | result = append(result, element) | ||
276 | } | ||
277 | default: | ||
278 | result = append(result, v) | ||
279 | } | ||
280 | } | ||
281 | |||
282 | w.replaceCurrent(reflect.ValueOf(result)) | ||
283 | } | ||