diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go new file mode 100644 index 0000000..f958bbc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go | |||
@@ -0,0 +1,333 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strconv" | ||
6 | "strings" | ||
7 | "sync" | ||
8 | |||
9 | "github.com/hashicorp/terraform/terraform" | ||
10 | "github.com/mitchellh/mapstructure" | ||
11 | ) | ||
12 | |||
13 | // ConfigFieldReader reads fields out of an untyped map[string]string to the | ||
14 | // best of its ability. It also applies defaults from the Schema. (The other | ||
15 | // field readers do not need default handling because they source fully | ||
16 | // populated data structures.) | ||
17 | type ConfigFieldReader struct { | ||
18 | Config *terraform.ResourceConfig | ||
19 | Schema map[string]*Schema | ||
20 | |||
21 | indexMaps map[string]map[string]int | ||
22 | once sync.Once | ||
23 | } | ||
24 | |||
25 | func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) { | ||
26 | r.once.Do(func() { r.indexMaps = make(map[string]map[string]int) }) | ||
27 | return r.readField(address, false) | ||
28 | } | ||
29 | |||
30 | func (r *ConfigFieldReader) readField( | ||
31 | address []string, nested bool) (FieldReadResult, error) { | ||
32 | schemaList := addrToSchema(address, r.Schema) | ||
33 | if len(schemaList) == 0 { | ||
34 | return FieldReadResult{}, nil | ||
35 | } | ||
36 | |||
37 | if !nested { | ||
38 | // If we have a set anywhere in the address, then we need to | ||
39 | // read that set out in order and actually replace that part of | ||
40 | // the address with the real list index. i.e. set.50 might actually | ||
41 | // map to set.12 in the config, since it is in list order in the | ||
42 | // config, not indexed by set value. | ||
43 | for i, v := range schemaList { | ||
44 | // Sets are the only thing that cause this issue. | ||
45 | if v.Type != TypeSet { | ||
46 | continue | ||
47 | } | ||
48 | |||
49 | // If we're at the end of the list, then we don't have to worry | ||
50 | // about this because we're just requesting the whole set. | ||
51 | if i == len(schemaList)-1 { | ||
52 | continue | ||
53 | } | ||
54 | |||
55 | // If we're looking for the count, then ignore... | ||
56 | if address[i+1] == "#" { | ||
57 | continue | ||
58 | } | ||
59 | |||
60 | indexMap, ok := r.indexMaps[strings.Join(address[:i+1], ".")] | ||
61 | if !ok { | ||
62 | // Get the set so we can get the index map that tells us the | ||
63 | // mapping of the hash code to the list index | ||
64 | _, err := r.readSet(address[:i+1], v) | ||
65 | if err != nil { | ||
66 | return FieldReadResult{}, err | ||
67 | } | ||
68 | indexMap = r.indexMaps[strings.Join(address[:i+1], ".")] | ||
69 | } | ||
70 | |||
71 | index, ok := indexMap[address[i+1]] | ||
72 | if !ok { | ||
73 | return FieldReadResult{}, nil | ||
74 | } | ||
75 | |||
76 | address[i+1] = strconv.FormatInt(int64(index), 10) | ||
77 | } | ||
78 | } | ||
79 | |||
80 | k := strings.Join(address, ".") | ||
81 | schema := schemaList[len(schemaList)-1] | ||
82 | |||
83 | // If we're getting the single element of a promoted list, then | ||
84 | // check to see if we have a single element we need to promote. | ||
85 | if address[len(address)-1] == "0" && len(schemaList) > 1 { | ||
86 | lastSchema := schemaList[len(schemaList)-2] | ||
87 | if lastSchema.Type == TypeList && lastSchema.PromoteSingle { | ||
88 | k := strings.Join(address[:len(address)-1], ".") | ||
89 | result, err := r.readPrimitive(k, schema) | ||
90 | if err == nil { | ||
91 | return result, nil | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | switch schema.Type { | ||
97 | case TypeBool, TypeFloat, TypeInt, TypeString: | ||
98 | return r.readPrimitive(k, schema) | ||
99 | case TypeList: | ||
100 | // If we support promotion then we first check if we have a lone | ||
101 | // value that we must promote. | ||
102 | // a value that is alone. | ||
103 | if schema.PromoteSingle { | ||
104 | result, err := r.readPrimitive(k, schema.Elem.(*Schema)) | ||
105 | if err == nil && result.Exists { | ||
106 | result.Value = []interface{}{result.Value} | ||
107 | return result, nil | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return readListField(&nestedConfigFieldReader{r}, address, schema) | ||
112 | case TypeMap: | ||
113 | return r.readMap(k, schema) | ||
114 | case TypeSet: | ||
115 | return r.readSet(address, schema) | ||
116 | case typeObject: | ||
117 | return readObjectField( | ||
118 | &nestedConfigFieldReader{r}, | ||
119 | address, schema.Elem.(map[string]*Schema)) | ||
120 | default: | ||
121 | panic(fmt.Sprintf("Unknown type: %s", schema.Type)) | ||
122 | } | ||
123 | } | ||
124 | |||
125 | func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) { | ||
126 | // We want both the raw value and the interpolated. We use the interpolated | ||
127 | // to store actual values and we use the raw one to check for | ||
128 | // computed keys. Actual values are obtained in the switch, depending on | ||
129 | // the type of the raw value. | ||
130 | mraw, ok := r.Config.GetRaw(k) | ||
131 | if !ok { | ||
132 | // check if this is from an interpolated field by seeing if it exists | ||
133 | // in the config | ||
134 | _, ok := r.Config.Get(k) | ||
135 | if !ok { | ||
136 | // this really doesn't exist | ||
137 | return FieldReadResult{}, nil | ||
138 | } | ||
139 | |||
140 | // We couldn't fetch the value from a nested data structure, so treat the | ||
141 | // raw value as an interpolation string. The mraw value is only used | ||
142 | // for the type switch below. | ||
143 | mraw = "${INTERPOLATED}" | ||
144 | } | ||
145 | |||
146 | result := make(map[string]interface{}) | ||
147 | computed := false | ||
148 | switch m := mraw.(type) { | ||
149 | case string: | ||
150 | // This is a map which has come out of an interpolated variable, so we | ||
151 | // can just get the value directly from config. Values cannot be computed | ||
152 | // currently. | ||
153 | v, _ := r.Config.Get(k) | ||
154 | |||
155 | // If this isn't a map[string]interface, it must be computed. | ||
156 | mapV, ok := v.(map[string]interface{}) | ||
157 | if !ok { | ||
158 | return FieldReadResult{ | ||
159 | Exists: true, | ||
160 | Computed: true, | ||
161 | }, nil | ||
162 | } | ||
163 | |||
164 | // Otherwise we can proceed as usual. | ||
165 | for i, iv := range mapV { | ||
166 | result[i] = iv | ||
167 | } | ||
168 | case []interface{}: | ||
169 | for i, innerRaw := range m { | ||
170 | for ik := range innerRaw.(map[string]interface{}) { | ||
171 | key := fmt.Sprintf("%s.%d.%s", k, i, ik) | ||
172 | if r.Config.IsComputed(key) { | ||
173 | computed = true | ||
174 | break | ||
175 | } | ||
176 | |||
177 | v, _ := r.Config.Get(key) | ||
178 | result[ik] = v | ||
179 | } | ||
180 | } | ||
181 | case []map[string]interface{}: | ||
182 | for i, innerRaw := range m { | ||
183 | for ik := range innerRaw { | ||
184 | key := fmt.Sprintf("%s.%d.%s", k, i, ik) | ||
185 | if r.Config.IsComputed(key) { | ||
186 | computed = true | ||
187 | break | ||
188 | } | ||
189 | |||
190 | v, _ := r.Config.Get(key) | ||
191 | result[ik] = v | ||
192 | } | ||
193 | } | ||
194 | case map[string]interface{}: | ||
195 | for ik := range m { | ||
196 | key := fmt.Sprintf("%s.%s", k, ik) | ||
197 | if r.Config.IsComputed(key) { | ||
198 | computed = true | ||
199 | break | ||
200 | } | ||
201 | |||
202 | v, _ := r.Config.Get(key) | ||
203 | result[ik] = v | ||
204 | } | ||
205 | default: | ||
206 | panic(fmt.Sprintf("unknown type: %#v", mraw)) | ||
207 | } | ||
208 | |||
209 | err := mapValuesToPrimitive(result, schema) | ||
210 | if err != nil { | ||
211 | return FieldReadResult{}, nil | ||
212 | } | ||
213 | |||
214 | var value interface{} | ||
215 | if !computed { | ||
216 | value = result | ||
217 | } | ||
218 | |||
219 | return FieldReadResult{ | ||
220 | Value: value, | ||
221 | Exists: true, | ||
222 | Computed: computed, | ||
223 | }, nil | ||
224 | } | ||
225 | |||
226 | func (r *ConfigFieldReader) readPrimitive( | ||
227 | k string, schema *Schema) (FieldReadResult, error) { | ||
228 | raw, ok := r.Config.Get(k) | ||
229 | if !ok { | ||
230 | // Nothing in config, but we might still have a default from the schema | ||
231 | var err error | ||
232 | raw, err = schema.DefaultValue() | ||
233 | if err != nil { | ||
234 | return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err) | ||
235 | } | ||
236 | |||
237 | if raw == nil { | ||
238 | return FieldReadResult{}, nil | ||
239 | } | ||
240 | } | ||
241 | |||
242 | var result string | ||
243 | if err := mapstructure.WeakDecode(raw, &result); err != nil { | ||
244 | return FieldReadResult{}, err | ||
245 | } | ||
246 | |||
247 | computed := r.Config.IsComputed(k) | ||
248 | returnVal, err := stringToPrimitive(result, computed, schema) | ||
249 | if err != nil { | ||
250 | return FieldReadResult{}, err | ||
251 | } | ||
252 | |||
253 | return FieldReadResult{ | ||
254 | Value: returnVal, | ||
255 | Exists: true, | ||
256 | Computed: computed, | ||
257 | }, nil | ||
258 | } | ||
259 | |||
260 | func (r *ConfigFieldReader) readSet( | ||
261 | address []string, schema *Schema) (FieldReadResult, error) { | ||
262 | indexMap := make(map[string]int) | ||
263 | // Create the set that will be our result | ||
264 | set := schema.ZeroValue().(*Set) | ||
265 | |||
266 | raw, err := readListField(&nestedConfigFieldReader{r}, address, schema) | ||
267 | if err != nil { | ||
268 | return FieldReadResult{}, err | ||
269 | } | ||
270 | if !raw.Exists { | ||
271 | return FieldReadResult{Value: set}, nil | ||
272 | } | ||
273 | |||
274 | // If the list is computed, the set is necessarilly computed | ||
275 | if raw.Computed { | ||
276 | return FieldReadResult{ | ||
277 | Value: set, | ||
278 | Exists: true, | ||
279 | Computed: raw.Computed, | ||
280 | }, nil | ||
281 | } | ||
282 | |||
283 | // Build up the set from the list elements | ||
284 | for i, v := range raw.Value.([]interface{}) { | ||
285 | // Check if any of the keys in this item are computed | ||
286 | computed := r.hasComputedSubKeys( | ||
287 | fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema) | ||
288 | |||
289 | code := set.add(v, computed) | ||
290 | indexMap[code] = i | ||
291 | } | ||
292 | |||
293 | r.indexMaps[strings.Join(address, ".")] = indexMap | ||
294 | |||
295 | return FieldReadResult{ | ||
296 | Value: set, | ||
297 | Exists: true, | ||
298 | }, nil | ||
299 | } | ||
300 | |||
301 | // hasComputedSubKeys walks through a schema and returns whether or not the | ||
302 | // given key contains any subkeys that are computed. | ||
303 | func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool { | ||
304 | prefix := key + "." | ||
305 | |||
306 | switch t := schema.Elem.(type) { | ||
307 | case *Resource: | ||
308 | for k, schema := range t.Schema { | ||
309 | if r.Config.IsComputed(prefix + k) { | ||
310 | return true | ||
311 | } | ||
312 | |||
313 | if r.hasComputedSubKeys(prefix+k, schema) { | ||
314 | return true | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | |||
319 | return false | ||
320 | } | ||
321 | |||
322 | // nestedConfigFieldReader is a funny little thing that just wraps a | ||
323 | // ConfigFieldReader to call readField when ReadField is called so that | ||
324 | // we don't recalculate the set rewrites in the address, which leads to | ||
325 | // an infinite loop. | ||
326 | type nestedConfigFieldReader struct { | ||
327 | Reader *ConfigFieldReader | ||
328 | } | ||
329 | |||
330 | func (r *nestedConfigFieldReader) ReadField( | ||
331 | address []string) (FieldReadResult, error) { | ||
332 | return r.Reader.readField(address, true) | ||
333 | } | ||