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