]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / field_reader_config.go
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 }