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