github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/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 switch schema.Type { 83 case TypeBool, TypeFloat, TypeInt, TypeString: 84 return r.readPrimitive(k, schema) 85 case TypeList: 86 return readListField(&nestedConfigFieldReader{r}, address, schema) 87 case TypeMap: 88 return r.readMap(k, schema) 89 case TypeSet: 90 return r.readSet(address, schema) 91 case typeObject: 92 return readObjectField( 93 &nestedConfigFieldReader{r}, 94 address, schema.Elem.(map[string]*Schema)) 95 default: 96 panic(fmt.Sprintf("Unknown type: %s", schema.Type)) 97 } 98 } 99 100 func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) { 101 // We want both the raw value and the interpolated. We use the interpolated 102 // to store actual values and we use the raw one to check for 103 // computed keys. Actual values are obtained in the switch, depending on 104 // the type of the raw value. 105 mraw, ok := r.Config.GetRaw(k) 106 if !ok { 107 return FieldReadResult{}, nil 108 } 109 110 result := make(map[string]interface{}) 111 computed := false 112 switch m := mraw.(type) { 113 case string: 114 // This is a map which has come out of an interpolated variable, so we 115 // can just get the value directly from config. Values cannot be computed 116 // currently. 117 v, _ := r.Config.Get(k) 118 119 // If this isn't a map[string]interface, it must be computed. 120 mapV, ok := v.(map[string]interface{}) 121 if !ok { 122 return FieldReadResult{ 123 Exists: true, 124 Computed: true, 125 }, nil 126 } 127 128 // Otherwise we can proceed as usual. 129 for i, iv := range mapV { 130 result[i] = iv 131 } 132 case []interface{}: 133 for i, innerRaw := range m { 134 for ik := range innerRaw.(map[string]interface{}) { 135 key := fmt.Sprintf("%s.%d.%s", k, i, ik) 136 if r.Config.IsComputed(key) { 137 computed = true 138 break 139 } 140 141 v, _ := r.Config.Get(key) 142 result[ik] = v 143 } 144 } 145 case []map[string]interface{}: 146 for i, innerRaw := range m { 147 for ik := range innerRaw { 148 key := fmt.Sprintf("%s.%d.%s", k, i, ik) 149 if r.Config.IsComputed(key) { 150 computed = true 151 break 152 } 153 154 v, _ := r.Config.Get(key) 155 result[ik] = v 156 } 157 } 158 case map[string]interface{}: 159 for ik := range m { 160 key := fmt.Sprintf("%s.%s", k, ik) 161 if r.Config.IsComputed(key) { 162 computed = true 163 break 164 } 165 166 v, _ := r.Config.Get(key) 167 result[ik] = v 168 } 169 default: 170 panic(fmt.Sprintf("unknown type: %#v", mraw)) 171 } 172 173 err := mapValuesToPrimitive(result, schema) 174 if err != nil { 175 return FieldReadResult{}, nil 176 } 177 178 var value interface{} 179 if !computed { 180 value = result 181 } 182 183 return FieldReadResult{ 184 Value: value, 185 Exists: true, 186 Computed: computed, 187 }, nil 188 } 189 190 func (r *ConfigFieldReader) readPrimitive( 191 k string, schema *Schema) (FieldReadResult, error) { 192 raw, ok := r.Config.Get(k) 193 if !ok { 194 // Nothing in config, but we might still have a default from the schema 195 var err error 196 raw, err = schema.DefaultValue() 197 if err != nil { 198 return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err) 199 } 200 201 if raw == nil { 202 return FieldReadResult{}, nil 203 } 204 } 205 206 var result string 207 if err := mapstructure.WeakDecode(raw, &result); err != nil { 208 return FieldReadResult{}, err 209 } 210 211 computed := r.Config.IsComputed(k) 212 returnVal, err := stringToPrimitive(result, computed, schema) 213 if err != nil { 214 return FieldReadResult{}, err 215 } 216 217 return FieldReadResult{ 218 Value: returnVal, 219 Exists: true, 220 Computed: computed, 221 }, nil 222 } 223 224 func (r *ConfigFieldReader) readSet( 225 address []string, schema *Schema) (FieldReadResult, error) { 226 indexMap := make(map[string]int) 227 // Create the set that will be our result 228 set := schema.ZeroValue().(*Set) 229 230 raw, err := readListField(&nestedConfigFieldReader{r}, address, schema) 231 if err != nil { 232 return FieldReadResult{}, err 233 } 234 if !raw.Exists { 235 return FieldReadResult{Value: set}, nil 236 } 237 238 // If the list is computed, the set is necessarilly computed 239 if raw.Computed { 240 return FieldReadResult{ 241 Value: set, 242 Exists: true, 243 Computed: raw.Computed, 244 }, nil 245 } 246 247 // Build up the set from the list elements 248 for i, v := range raw.Value.([]interface{}) { 249 // Check if any of the keys in this item are computed 250 computed := r.hasComputedSubKeys( 251 fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema) 252 253 code := set.add(v, computed) 254 indexMap[code] = i 255 } 256 257 r.indexMaps[strings.Join(address, ".")] = indexMap 258 259 return FieldReadResult{ 260 Value: set, 261 Exists: true, 262 }, nil 263 } 264 265 // hasComputedSubKeys walks through a schema and returns whether or not the 266 // given key contains any subkeys that are computed. 267 func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool { 268 prefix := key + "." 269 270 switch t := schema.Elem.(type) { 271 case *Resource: 272 for k, schema := range t.Schema { 273 if r.Config.IsComputed(prefix + k) { 274 return true 275 } 276 277 if r.hasComputedSubKeys(prefix+k, schema) { 278 return true 279 } 280 } 281 } 282 283 return false 284 } 285 286 // nestedConfigFieldReader is a funny little thing that just wraps a 287 // ConfigFieldReader to call readField when ReadField is called so that 288 // we don't recalculate the set rewrites in the address, which leads to 289 // an infinite loop. 290 type nestedConfigFieldReader struct { 291 Reader *ConfigFieldReader 292 } 293 294 func (r *nestedConfigFieldReader) ReadField( 295 address []string) (FieldReadResult, error) { 296 return r.Reader.readField(address, true) 297 }