github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/helper/schema/field_reader_config.go (about) 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 }