github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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 14 // the best of its ability. 15 type ConfigFieldReader struct { 16 Config *terraform.ResourceConfig 17 Schema map[string]*Schema 18 19 lock sync.Mutex 20 } 21 22 func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) { 23 return r.readField(address, false) 24 } 25 26 func (r *ConfigFieldReader) readField( 27 address []string, nested bool) (FieldReadResult, error) { 28 schemaList := addrToSchema(address, r.Schema) 29 if len(schemaList) == 0 { 30 return FieldReadResult{}, nil 31 } 32 33 if !nested { 34 // If we have a set anywhere in the address, then we need to 35 // read that set out in order and actually replace that part of 36 // the address with the real list index. i.e. set.50 might actually 37 // map to set.12 in the config, since it is in list order in the 38 // config, not indexed by set value. 39 for i, v := range schemaList { 40 // Sets are the only thing that cause this issue. 41 if v.Type != TypeSet { 42 continue 43 } 44 45 // If we're at the end of the list, then we don't have to worry 46 // about this because we're just requesting the whole set. 47 if i == len(schemaList)-1 { 48 continue 49 } 50 51 // If we're looking for the count, then ignore... 52 if address[i+1] == "#" { 53 continue 54 } 55 56 // Get the code 57 code, err := strconv.ParseInt(address[i+1], 0, 0) 58 if err != nil { 59 return FieldReadResult{}, err 60 } 61 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 _, indexMap, err := r.readSet(address[:i+1], v) 65 if err != nil { 66 return FieldReadResult{}, err 67 } 68 69 index, ok := indexMap[int(code)] 70 if !ok { 71 return FieldReadResult{}, nil 72 } 73 74 address[i+1] = strconv.FormatInt(int64(index), 10) 75 } 76 } 77 78 k := strings.Join(address, ".") 79 schema := schemaList[len(schemaList)-1] 80 switch schema.Type { 81 case TypeBool: 82 fallthrough 83 case TypeFloat: 84 fallthrough 85 case TypeInt: 86 fallthrough 87 case TypeString: 88 return r.readPrimitive(k, schema) 89 case TypeList: 90 return readListField(&nestedConfigFieldReader{r}, address, schema) 91 case TypeMap: 92 return r.readMap(k) 93 case TypeSet: 94 result, _, err := r.readSet(address, schema) 95 return result, err 96 case typeObject: 97 return readObjectField( 98 &nestedConfigFieldReader{r}, 99 address, schema.Elem.(map[string]*Schema)) 100 default: 101 panic(fmt.Sprintf("Unknown type: %s", schema.Type)) 102 } 103 } 104 105 func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) { 106 mraw, ok := r.Config.Get(k) 107 if !ok { 108 return FieldReadResult{}, nil 109 } 110 111 result := make(map[string]interface{}) 112 switch m := mraw.(type) { 113 case []interface{}: 114 for _, innerRaw := range m { 115 for k, v := range innerRaw.(map[string]interface{}) { 116 result[k] = v 117 } 118 } 119 case []map[string]interface{}: 120 for _, innerRaw := range m { 121 for k, v := range innerRaw { 122 result[k] = v 123 } 124 } 125 case map[string]interface{}: 126 result = m 127 default: 128 panic(fmt.Sprintf("unknown type: %#v", mraw)) 129 } 130 131 return FieldReadResult{ 132 Value: result, 133 Exists: true, 134 }, nil 135 } 136 137 func (r *ConfigFieldReader) readPrimitive( 138 k string, schema *Schema) (FieldReadResult, error) { 139 raw, ok := r.Config.Get(k) 140 if !ok { 141 return FieldReadResult{}, nil 142 } 143 144 var result string 145 if err := mapstructure.WeakDecode(raw, &result); err != nil { 146 return FieldReadResult{}, err 147 } 148 149 computed := r.Config.IsComputed(k) 150 returnVal, err := stringToPrimitive(result, computed, schema) 151 if err != nil { 152 return FieldReadResult{}, err 153 } 154 155 return FieldReadResult{ 156 Value: returnVal, 157 Exists: true, 158 Computed: computed, 159 }, nil 160 } 161 162 func (r *ConfigFieldReader) readSet( 163 address []string, schema *Schema) (FieldReadResult, map[int]int, error) { 164 indexMap := make(map[int]int) 165 // Create the set that will be our result 166 set := &Set{F: schema.Set} 167 168 raw, err := readListField(&nestedConfigFieldReader{r}, address, schema) 169 if err != nil { 170 return FieldReadResult{}, indexMap, err 171 } 172 if !raw.Exists { 173 return FieldReadResult{Value: set}, indexMap, nil 174 } 175 176 // If the list is computed, the set is necessarilly computed 177 if raw.Computed { 178 return FieldReadResult{ 179 Value: set, 180 Exists: true, 181 Computed: raw.Computed, 182 }, indexMap, nil 183 } 184 185 // Build up the set from the list elements 186 for i, v := range raw.Value.([]interface{}) { 187 // Check if any of the keys in this item are computed 188 computed := r.hasComputedSubKeys( 189 fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema) 190 191 code := set.add(v) 192 indexMap[code] = i 193 if computed { 194 set.m[-code] = set.m[code] 195 delete(set.m, code) 196 code = -code 197 } 198 } 199 200 return FieldReadResult{ 201 Value: set, 202 Exists: true, 203 }, indexMap, nil 204 } 205 206 // hasComputedSubKeys walks through a schema and returns whether or not the 207 // given key contains any subkeys that are computed. 208 func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool { 209 prefix := key + "." 210 211 switch t := schema.Elem.(type) { 212 case *Resource: 213 for k, schema := range t.Schema { 214 if r.Config.IsComputed(prefix + k) { 215 return true 216 } 217 218 if r.hasComputedSubKeys(prefix+k, schema) { 219 return true 220 } 221 } 222 } 223 224 return false 225 } 226 227 // nestedConfigFieldReader is a funny little thing that just wraps a 228 // ConfigFieldReader to call readField when ReadField is called so that 229 // we don't recalculate the set rewrites in the address, which leads to 230 // an infinite loop. 231 type nestedConfigFieldReader struct { 232 Reader *ConfigFieldReader 233 } 234 235 func (r *nestedConfigFieldReader) ReadField( 236 address []string) (FieldReadResult, error) { 237 return r.Reader.readField(address, true) 238 }