github.com/alexissmirnov/terraform@v0.4.3-0.20150423153700-1ef9731a2f14/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 lock sync.Mutex 22 } 23 24 func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) { 25 return r.readField(address, false) 26 } 27 28 func (r *ConfigFieldReader) readField( 29 address []string, nested bool) (FieldReadResult, error) { 30 schemaList := addrToSchema(address, r.Schema) 31 if len(schemaList) == 0 { 32 return FieldReadResult{}, nil 33 } 34 35 if !nested { 36 // If we have a set anywhere in the address, then we need to 37 // read that set out in order and actually replace that part of 38 // the address with the real list index. i.e. set.50 might actually 39 // map to set.12 in the config, since it is in list order in the 40 // config, not indexed by set value. 41 for i, v := range schemaList { 42 // Sets are the only thing that cause this issue. 43 if v.Type != TypeSet { 44 continue 45 } 46 47 // If we're at the end of the list, then we don't have to worry 48 // about this because we're just requesting the whole set. 49 if i == len(schemaList)-1 { 50 continue 51 } 52 53 // If we're looking for the count, then ignore... 54 if address[i+1] == "#" { 55 continue 56 } 57 58 // Get the code 59 code, err := strconv.ParseInt(address[i+1], 0, 0) 60 if err != nil { 61 return FieldReadResult{}, err 62 } 63 64 // Get the set so we can get the index map that tells us the 65 // mapping of the hash code to the list index 66 _, indexMap, err := r.readSet(address[:i+1], v) 67 if err != nil { 68 return FieldReadResult{}, err 69 } 70 71 index, ok := indexMap[int(code)] 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: 84 fallthrough 85 case TypeFloat: 86 fallthrough 87 case TypeInt: 88 fallthrough 89 case TypeString: 90 return r.readPrimitive(k, schema) 91 case TypeList: 92 return readListField(&nestedConfigFieldReader{r}, address, schema) 93 case TypeMap: 94 return r.readMap(k) 95 case TypeSet: 96 result, _, err := r.readSet(address, schema) 97 return result, err 98 case typeObject: 99 return readObjectField( 100 &nestedConfigFieldReader{r}, 101 address, schema.Elem.(map[string]*Schema)) 102 default: 103 panic(fmt.Sprintf("Unknown type: %s", schema.Type)) 104 } 105 } 106 107 func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) { 108 // We want both the raw value and the interpolated. We use the interpolated 109 // to store actual values and we use the raw one to check for 110 // computed keys. 111 mraw, ok := r.Config.GetRaw(k) 112 if !ok { 113 return FieldReadResult{}, nil 114 } 115 116 result := make(map[string]interface{}) 117 computed := false 118 switch m := mraw.(type) { 119 case []interface{}: 120 for i, innerRaw := range m { 121 for ik, _ := range innerRaw.(map[string]interface{}) { 122 key := fmt.Sprintf("%s.%d.%s", k, i, ik) 123 if r.Config.IsComputed(key) { 124 computed = true 125 break 126 } 127 128 v, _ := r.Config.Get(key) 129 result[ik] = v 130 } 131 } 132 case []map[string]interface{}: 133 for i, innerRaw := range m { 134 for ik, _ := range innerRaw { 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 ik, _ := range m { 147 key := fmt.Sprintf("%s.%s", k, ik) 148 if r.Config.IsComputed(key) { 149 computed = true 150 break 151 } 152 153 v, _ := r.Config.Get(key) 154 result[ik] = v 155 } 156 default: 157 panic(fmt.Sprintf("unknown type: %#v", mraw)) 158 } 159 160 var value interface{} 161 if !computed { 162 value = result 163 } 164 165 return FieldReadResult{ 166 Value: value, 167 Exists: true, 168 Computed: computed, 169 }, nil 170 } 171 172 func (r *ConfigFieldReader) readPrimitive( 173 k string, schema *Schema) (FieldReadResult, error) { 174 raw, ok := r.Config.Get(k) 175 if !ok { 176 // Nothing in config, but we might still have a default from the schema 177 var err error 178 raw, err = schema.DefaultValue() 179 if err != nil { 180 return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err) 181 } 182 183 if raw == nil { 184 return FieldReadResult{}, nil 185 } 186 } 187 188 var result string 189 if err := mapstructure.WeakDecode(raw, &result); err != nil { 190 return FieldReadResult{}, err 191 } 192 193 computed := r.Config.IsComputed(k) 194 returnVal, err := stringToPrimitive(result, computed, schema) 195 if err != nil { 196 return FieldReadResult{}, err 197 } 198 199 return FieldReadResult{ 200 Value: returnVal, 201 Exists: true, 202 Computed: computed, 203 }, nil 204 } 205 206 func (r *ConfigFieldReader) readSet( 207 address []string, schema *Schema) (FieldReadResult, map[int]int, error) { 208 indexMap := make(map[int]int) 209 // Create the set that will be our result 210 set := &Set{F: schema.Set} 211 212 raw, err := readListField(&nestedConfigFieldReader{r}, address, schema) 213 if err != nil { 214 return FieldReadResult{}, indexMap, err 215 } 216 if !raw.Exists { 217 return FieldReadResult{Value: set}, indexMap, nil 218 } 219 220 // If the list is computed, the set is necessarilly computed 221 if raw.Computed { 222 return FieldReadResult{ 223 Value: set, 224 Exists: true, 225 Computed: raw.Computed, 226 }, indexMap, nil 227 } 228 229 // Build up the set from the list elements 230 for i, v := range raw.Value.([]interface{}) { 231 // Check if any of the keys in this item are computed 232 computed := r.hasComputedSubKeys( 233 fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema) 234 235 code := set.add(v) 236 indexMap[code] = i 237 if computed { 238 set.m[-code] = set.m[code] 239 delete(set.m, code) 240 code = -code 241 } 242 } 243 244 return FieldReadResult{ 245 Value: set, 246 Exists: true, 247 }, indexMap, nil 248 } 249 250 // hasComputedSubKeys walks through a schema and returns whether or not the 251 // given key contains any subkeys that are computed. 252 func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool { 253 prefix := key + "." 254 255 switch t := schema.Elem.(type) { 256 case *Resource: 257 for k, schema := range t.Schema { 258 if r.Config.IsComputed(prefix + k) { 259 return true 260 } 261 262 if r.hasComputedSubKeys(prefix+k, schema) { 263 return true 264 } 265 } 266 } 267 268 return false 269 } 270 271 // nestedConfigFieldReader is a funny little thing that just wraps a 272 // ConfigFieldReader to call readField when ReadField is called so that 273 // we don't recalculate the set rewrites in the address, which leads to 274 // an infinite loop. 275 type nestedConfigFieldReader struct { 276 Reader *ConfigFieldReader 277 } 278 279 func (r *nestedConfigFieldReader) ReadField( 280 address []string) (FieldReadResult, error) { 281 return r.Reader.readField(address, true) 282 }