github.com/jpedro/terraform@v0.11.12-beta1/helper/schema/field_writer_map.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "strings" 8 "sync" 9 10 "github.com/mitchellh/mapstructure" 11 ) 12 13 // MapFieldWriter writes data into a single map[string]string structure. 14 type MapFieldWriter struct { 15 Schema map[string]*Schema 16 17 lock sync.Mutex 18 result map[string]string 19 } 20 21 // Map returns the underlying map that is being written to. 22 func (w *MapFieldWriter) Map() map[string]string { 23 w.lock.Lock() 24 defer w.lock.Unlock() 25 if w.result == nil { 26 w.result = make(map[string]string) 27 } 28 29 return w.result 30 } 31 32 func (w *MapFieldWriter) unsafeWriteField(addr string, value string) { 33 w.lock.Lock() 34 defer w.lock.Unlock() 35 if w.result == nil { 36 w.result = make(map[string]string) 37 } 38 39 w.result[addr] = value 40 } 41 42 // clearTree clears a field and any sub-fields of the given address out of the 43 // map. This should be used to reset some kind of complex structures (namely 44 // sets) before writing to make sure that any conflicting data is removed (for 45 // example, if the set was previously written to the writer's layer). 46 func (w *MapFieldWriter) clearTree(addr []string) { 47 prefix := strings.Join(addr, ".") + "." 48 for k := range w.result { 49 if strings.HasPrefix(k, prefix) { 50 delete(w.result, k) 51 } 52 } 53 } 54 55 func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { 56 w.lock.Lock() 57 defer w.lock.Unlock() 58 if w.result == nil { 59 w.result = make(map[string]string) 60 } 61 62 schemaList := addrToSchema(addr, w.Schema) 63 if len(schemaList) == 0 { 64 return fmt.Errorf("Invalid address to set: %#v", addr) 65 } 66 67 // If we're setting anything other than a list root or set root, 68 // then disallow it. 69 for _, schema := range schemaList[:len(schemaList)-1] { 70 if schema.Type == TypeList { 71 return fmt.Errorf( 72 "%s: can only set full list", 73 strings.Join(addr, ".")) 74 } 75 76 if schema.Type == TypeMap { 77 return fmt.Errorf( 78 "%s: can only set full map", 79 strings.Join(addr, ".")) 80 } 81 82 if schema.Type == TypeSet { 83 return fmt.Errorf( 84 "%s: can only set full set", 85 strings.Join(addr, ".")) 86 } 87 } 88 89 return w.set(addr, value) 90 } 91 92 func (w *MapFieldWriter) set(addr []string, value interface{}) error { 93 schemaList := addrToSchema(addr, w.Schema) 94 if len(schemaList) == 0 { 95 return fmt.Errorf("Invalid address to set: %#v", addr) 96 } 97 98 schema := schemaList[len(schemaList)-1] 99 switch schema.Type { 100 case TypeBool, TypeInt, TypeFloat, TypeString: 101 return w.setPrimitive(addr, value, schema) 102 case TypeList: 103 return w.setList(addr, value, schema) 104 case TypeMap: 105 return w.setMap(addr, value, schema) 106 case TypeSet: 107 return w.setSet(addr, value, schema) 108 case typeObject: 109 return w.setObject(addr, value, schema) 110 default: 111 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 112 } 113 } 114 115 func (w *MapFieldWriter) setList( 116 addr []string, 117 v interface{}, 118 schema *Schema) error { 119 k := strings.Join(addr, ".") 120 setElement := func(idx string, value interface{}) error { 121 addrCopy := make([]string, len(addr), len(addr)+1) 122 copy(addrCopy, addr) 123 return w.set(append(addrCopy, idx), value) 124 } 125 126 var vs []interface{} 127 if err := mapstructure.Decode(v, &vs); err != nil { 128 return fmt.Errorf("%s: %s", k, err) 129 } 130 131 // Wipe the set from the current writer prior to writing if it exists. 132 // Multiple writes to the same layer is a lot safer for lists than sets due 133 // to the fact that indexes are always deterministic and the length will 134 // always be updated with the current length on the last write, but making 135 // sure we have a clean namespace removes any chance for edge cases to pop up 136 // and ensures that the last write to the set is the correct value. 137 w.clearTree(addr) 138 139 // Set the entire list. 140 var err error 141 for i, elem := range vs { 142 is := strconv.FormatInt(int64(i), 10) 143 err = setElement(is, elem) 144 if err != nil { 145 break 146 } 147 } 148 if err != nil { 149 for i, _ := range vs { 150 is := strconv.FormatInt(int64(i), 10) 151 setElement(is, nil) 152 } 153 154 return err 155 } 156 157 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10) 158 return nil 159 } 160 161 func (w *MapFieldWriter) setMap( 162 addr []string, 163 value interface{}, 164 schema *Schema) error { 165 k := strings.Join(addr, ".") 166 v := reflect.ValueOf(value) 167 vs := make(map[string]interface{}) 168 169 if value == nil { 170 // The empty string here means the map is removed. 171 w.result[k] = "" 172 return nil 173 } 174 175 if v.Kind() != reflect.Map { 176 return fmt.Errorf("%s: must be a map", k) 177 } 178 if v.Type().Key().Kind() != reflect.String { 179 return fmt.Errorf("%s: keys must strings", k) 180 } 181 for _, mk := range v.MapKeys() { 182 mv := v.MapIndex(mk) 183 vs[mk.String()] = mv.Interface() 184 } 185 186 // Wipe this address tree. The contents of the map should always reflect the 187 // last write made to it. 188 w.clearTree(addr) 189 190 // Remove the pure key since we're setting the full map value 191 delete(w.result, k) 192 193 // Set each subkey 194 addrCopy := make([]string, len(addr), len(addr)+1) 195 copy(addrCopy, addr) 196 for subKey, v := range vs { 197 if err := w.set(append(addrCopy, subKey), v); err != nil { 198 return err 199 } 200 } 201 202 // Set the count 203 w.result[k+".%"] = strconv.Itoa(len(vs)) 204 205 return nil 206 } 207 208 func (w *MapFieldWriter) setObject( 209 addr []string, 210 value interface{}, 211 schema *Schema) error { 212 // Set the entire object. First decode into a proper structure 213 var v map[string]interface{} 214 if err := mapstructure.Decode(value, &v); err != nil { 215 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err) 216 } 217 218 // Make space for additional elements in the address 219 addrCopy := make([]string, len(addr), len(addr)+1) 220 copy(addrCopy, addr) 221 222 // Set each element in turn 223 var err error 224 for k1, v1 := range v { 225 if err = w.set(append(addrCopy, k1), v1); err != nil { 226 break 227 } 228 } 229 if err != nil { 230 for k1, _ := range v { 231 w.set(append(addrCopy, k1), nil) 232 } 233 } 234 235 return err 236 } 237 238 func (w *MapFieldWriter) setPrimitive( 239 addr []string, 240 v interface{}, 241 schema *Schema) error { 242 k := strings.Join(addr, ".") 243 244 if v == nil { 245 // The empty string here means the value is removed. 246 w.result[k] = "" 247 return nil 248 } 249 250 var set string 251 switch schema.Type { 252 case TypeBool: 253 var b bool 254 if err := mapstructure.Decode(v, &b); err != nil { 255 return fmt.Errorf("%s: %s", k, err) 256 } 257 258 set = strconv.FormatBool(b) 259 case TypeString: 260 if err := mapstructure.Decode(v, &set); err != nil { 261 return fmt.Errorf("%s: %s", k, err) 262 } 263 case TypeInt: 264 var n int 265 if err := mapstructure.Decode(v, &n); err != nil { 266 return fmt.Errorf("%s: %s", k, err) 267 } 268 set = strconv.FormatInt(int64(n), 10) 269 case TypeFloat: 270 var n float64 271 if err := mapstructure.Decode(v, &n); err != nil { 272 return fmt.Errorf("%s: %s", k, err) 273 } 274 set = strconv.FormatFloat(float64(n), 'G', -1, 64) 275 default: 276 return fmt.Errorf("Unknown type: %#v", schema.Type) 277 } 278 279 w.result[k] = set 280 return nil 281 } 282 283 func (w *MapFieldWriter) setSet( 284 addr []string, 285 value interface{}, 286 schema *Schema) error { 287 addrCopy := make([]string, len(addr), len(addr)+1) 288 copy(addrCopy, addr) 289 k := strings.Join(addr, ".") 290 291 if value == nil { 292 w.result[k+".#"] = "0" 293 return nil 294 } 295 296 // If it is a slice, then we have to turn it into a *Set so that 297 // we get the proper order back based on the hash code. 298 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { 299 // Build a temp *ResourceData to use for the conversion 300 tempSchema := *schema 301 tempSchema.Type = TypeList 302 tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema} 303 tempW := &MapFieldWriter{Schema: tempSchemaMap} 304 305 // Set the entire list, this lets us get sane values out of it 306 if err := tempW.WriteField(addr, value); err != nil { 307 return err 308 } 309 310 // Build the set by going over the list items in order and 311 // hashing them into the set. The reason we go over the list and 312 // not the `value` directly is because this forces all types 313 // to become []interface{} (generic) instead of []string, which 314 // most hash functions are expecting. 315 s := schema.ZeroValue().(*Set) 316 tempR := &MapFieldReader{ 317 Map: BasicMapReader(tempW.Map()), 318 Schema: tempSchemaMap, 319 } 320 for i := 0; i < v.Len(); i++ { 321 is := strconv.FormatInt(int64(i), 10) 322 result, err := tempR.ReadField(append(addrCopy, is)) 323 if err != nil { 324 return err 325 } 326 if !result.Exists { 327 panic("set item just set doesn't exist") 328 } 329 330 s.Add(result.Value) 331 } 332 333 value = s 334 } 335 336 // Clear any keys that match the set address first. This is necessary because 337 // it's always possible and sometimes may be necessary to write to a certain 338 // writer layer more than once with different set data each time, which will 339 // lead to different keys being inserted, which can lead to determinism 340 // problems when the old data isn't wiped first. 341 w.clearTree(addr) 342 343 for code, elem := range value.(*Set).m { 344 if err := w.set(append(addrCopy, code), elem); err != nil { 345 return err 346 } 347 } 348 349 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len()) 350 return nil 351 }