9 "github.com/hashicorp/terraform/terraform"
10 "github.com/mitchellh/mapstructure"
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
21 indexMaps map[string]map[string]int
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)
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
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 {
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 {
55 // If we're looking for the count, then ignore...
56 if address[i+1] == "#" {
60 indexMap, ok := r.indexMaps[strings.Join(address[:i+1], ".")]
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)
66 return FieldReadResult{}, err
68 indexMap = r.indexMaps[strings.Join(address[:i+1], ".")]
71 index, ok := indexMap[address[i+1]]
73 return FieldReadResult{}, nil
76 address[i+1] = strconv.FormatInt(int64(index), 10)
80 k := strings.Join(address, ".")
81 schema := schemaList[len(schemaList)-1]
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)
97 case TypeBool, TypeFloat, TypeInt, TypeString:
98 return r.readPrimitive(k, schema)
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}
111 return readListField(&nestedConfigFieldReader{r}, address, schema)
113 return r.readMap(k, schema)
115 return r.readSet(address, schema)
117 return readObjectField(
118 &nestedConfigFieldReader{r},
119 address, schema.Elem.(map[string]*Schema))
121 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
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)
132 // check if this is from an interpolated field by seeing if it exists
134 _, ok := r.Config.Get(k)
136 // this really doesn't exist
137 return FieldReadResult{}, nil
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}"
146 result := make(map[string]interface{})
148 switch m := mraw.(type) {
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
153 v, _ := r.Config.Get(k)
155 // If this isn't a map[string]interface, it must be computed.
156 mapV, ok := v.(map[string]interface{})
158 return FieldReadResult{
164 // Otherwise we can proceed as usual.
165 for i, iv := range mapV {
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) {
177 v, _ := r.Config.Get(key)
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) {
190 v, _ := r.Config.Get(key)
194 case map[string]interface{}:
196 key := fmt.Sprintf("%s.%s", k, ik)
197 if r.Config.IsComputed(key) {
202 v, _ := r.Config.Get(key)
206 panic(fmt.Sprintf("unknown type: %#v", mraw))
209 err := mapValuesToPrimitive(result, schema)
211 return FieldReadResult{}, nil
214 var value interface{}
219 return FieldReadResult{
226 func (r *ConfigFieldReader) readPrimitive(
227 k string, schema *Schema) (FieldReadResult, error) {
228 raw, ok := r.Config.Get(k)
230 // Nothing in config, but we might still have a default from the schema
232 raw, err = schema.DefaultValue()
234 return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err)
238 return FieldReadResult{}, nil
243 if err := mapstructure.WeakDecode(raw, &result); err != nil {
244 return FieldReadResult{}, err
247 computed := r.Config.IsComputed(k)
248 returnVal, err := stringToPrimitive(result, computed, schema)
250 return FieldReadResult{}, err
253 return FieldReadResult{
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)
266 raw, err := readListField(&nestedConfigFieldReader{r}, address, schema)
268 return FieldReadResult{}, err
271 return FieldReadResult{Value: set}, nil
274 // If the list is computed, the set is necessarilly computed
276 return FieldReadResult{
279 Computed: raw.Computed,
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)
289 code := set.add(v, computed)
293 r.indexMaps[strings.Join(address, ".")] = indexMap
295 return FieldReadResult{
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 {
306 switch t := schema.Elem.(type) {
308 for k, schema := range t.Schema {
309 if r.Config.IsComputed(prefix + k) {
313 if r.hasComputedSubKeys(prefix+k, schema) {
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
326 type nestedConfigFieldReader struct {
327 Reader *ConfigFieldReader
330 func (r *nestedConfigFieldReader) ReadField(
331 address []string) (FieldReadResult, error) {
332 return r.Reader.readField(address, true)