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