diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go new file mode 100644 index 0000000..689ed8d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go | |||
@@ -0,0 +1,319 @@ | |||
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 | func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { | ||
43 | w.lock.Lock() | ||
44 | defer w.lock.Unlock() | ||
45 | if w.result == nil { | ||
46 | w.result = make(map[string]string) | ||
47 | } | ||
48 | |||
49 | schemaList := addrToSchema(addr, w.Schema) | ||
50 | if len(schemaList) == 0 { | ||
51 | return fmt.Errorf("Invalid address to set: %#v", addr) | ||
52 | } | ||
53 | |||
54 | // If we're setting anything other than a list root or set root, | ||
55 | // then disallow it. | ||
56 | for _, schema := range schemaList[:len(schemaList)-1] { | ||
57 | if schema.Type == TypeList { | ||
58 | return fmt.Errorf( | ||
59 | "%s: can only set full list", | ||
60 | strings.Join(addr, ".")) | ||
61 | } | ||
62 | |||
63 | if schema.Type == TypeMap { | ||
64 | return fmt.Errorf( | ||
65 | "%s: can only set full map", | ||
66 | strings.Join(addr, ".")) | ||
67 | } | ||
68 | |||
69 | if schema.Type == TypeSet { | ||
70 | return fmt.Errorf( | ||
71 | "%s: can only set full set", | ||
72 | strings.Join(addr, ".")) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return w.set(addr, value) | ||
77 | } | ||
78 | |||
79 | func (w *MapFieldWriter) set(addr []string, value interface{}) error { | ||
80 | schemaList := addrToSchema(addr, w.Schema) | ||
81 | if len(schemaList) == 0 { | ||
82 | return fmt.Errorf("Invalid address to set: %#v", addr) | ||
83 | } | ||
84 | |||
85 | schema := schemaList[len(schemaList)-1] | ||
86 | switch schema.Type { | ||
87 | case TypeBool, TypeInt, TypeFloat, TypeString: | ||
88 | return w.setPrimitive(addr, value, schema) | ||
89 | case TypeList: | ||
90 | return w.setList(addr, value, schema) | ||
91 | case TypeMap: | ||
92 | return w.setMap(addr, value, schema) | ||
93 | case TypeSet: | ||
94 | return w.setSet(addr, value, schema) | ||
95 | case typeObject: | ||
96 | return w.setObject(addr, value, schema) | ||
97 | default: | ||
98 | panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) | ||
99 | } | ||
100 | } | ||
101 | |||
102 | func (w *MapFieldWriter) setList( | ||
103 | addr []string, | ||
104 | v interface{}, | ||
105 | schema *Schema) error { | ||
106 | k := strings.Join(addr, ".") | ||
107 | setElement := func(idx string, value interface{}) error { | ||
108 | addrCopy := make([]string, len(addr), len(addr)+1) | ||
109 | copy(addrCopy, addr) | ||
110 | return w.set(append(addrCopy, idx), value) | ||
111 | } | ||
112 | |||
113 | var vs []interface{} | ||
114 | if err := mapstructure.Decode(v, &vs); err != nil { | ||
115 | return fmt.Errorf("%s: %s", k, err) | ||
116 | } | ||
117 | |||
118 | // Set the entire list. | ||
119 | var err error | ||
120 | for i, elem := range vs { | ||
121 | is := strconv.FormatInt(int64(i), 10) | ||
122 | err = setElement(is, elem) | ||
123 | if err != nil { | ||
124 | break | ||
125 | } | ||
126 | } | ||
127 | if err != nil { | ||
128 | for i, _ := range vs { | ||
129 | is := strconv.FormatInt(int64(i), 10) | ||
130 | setElement(is, nil) | ||
131 | } | ||
132 | |||
133 | return err | ||
134 | } | ||
135 | |||
136 | w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10) | ||
137 | return nil | ||
138 | } | ||
139 | |||
140 | func (w *MapFieldWriter) setMap( | ||
141 | addr []string, | ||
142 | value interface{}, | ||
143 | schema *Schema) error { | ||
144 | k := strings.Join(addr, ".") | ||
145 | v := reflect.ValueOf(value) | ||
146 | vs := make(map[string]interface{}) | ||
147 | |||
148 | if value == nil { | ||
149 | // The empty string here means the map is removed. | ||
150 | w.result[k] = "" | ||
151 | return nil | ||
152 | } | ||
153 | |||
154 | if v.Kind() != reflect.Map { | ||
155 | return fmt.Errorf("%s: must be a map", k) | ||
156 | } | ||
157 | if v.Type().Key().Kind() != reflect.String { | ||
158 | return fmt.Errorf("%s: keys must strings", k) | ||
159 | } | ||
160 | for _, mk := range v.MapKeys() { | ||
161 | mv := v.MapIndex(mk) | ||
162 | vs[mk.String()] = mv.Interface() | ||
163 | } | ||
164 | |||
165 | // Remove the pure key since we're setting the full map value | ||
166 | delete(w.result, k) | ||
167 | |||
168 | // Set each subkey | ||
169 | addrCopy := make([]string, len(addr), len(addr)+1) | ||
170 | copy(addrCopy, addr) | ||
171 | for subKey, v := range vs { | ||
172 | if err := w.set(append(addrCopy, subKey), v); err != nil { | ||
173 | return err | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // Set the count | ||
178 | w.result[k+".%"] = strconv.Itoa(len(vs)) | ||
179 | |||
180 | return nil | ||
181 | } | ||
182 | |||
183 | func (w *MapFieldWriter) setObject( | ||
184 | addr []string, | ||
185 | value interface{}, | ||
186 | schema *Schema) error { | ||
187 | // Set the entire object. First decode into a proper structure | ||
188 | var v map[string]interface{} | ||
189 | if err := mapstructure.Decode(value, &v); err != nil { | ||
190 | return fmt.Errorf("%s: %s", strings.Join(addr, "."), err) | ||
191 | } | ||
192 | |||
193 | // Make space for additional elements in the address | ||
194 | addrCopy := make([]string, len(addr), len(addr)+1) | ||
195 | copy(addrCopy, addr) | ||
196 | |||
197 | // Set each element in turn | ||
198 | var err error | ||
199 | for k1, v1 := range v { | ||
200 | if err = w.set(append(addrCopy, k1), v1); err != nil { | ||
201 | break | ||
202 | } | ||
203 | } | ||
204 | if err != nil { | ||
205 | for k1, _ := range v { | ||
206 | w.set(append(addrCopy, k1), nil) | ||
207 | } | ||
208 | } | ||
209 | |||
210 | return err | ||
211 | } | ||
212 | |||
213 | func (w *MapFieldWriter) setPrimitive( | ||
214 | addr []string, | ||
215 | v interface{}, | ||
216 | schema *Schema) error { | ||
217 | k := strings.Join(addr, ".") | ||
218 | |||
219 | if v == nil { | ||
220 | // The empty string here means the value is removed. | ||
221 | w.result[k] = "" | ||
222 | return nil | ||
223 | } | ||
224 | |||
225 | var set string | ||
226 | switch schema.Type { | ||
227 | case TypeBool: | ||
228 | var b bool | ||
229 | if err := mapstructure.Decode(v, &b); err != nil { | ||
230 | return fmt.Errorf("%s: %s", k, err) | ||
231 | } | ||
232 | |||
233 | set = strconv.FormatBool(b) | ||
234 | case TypeString: | ||
235 | if err := mapstructure.Decode(v, &set); err != nil { | ||
236 | return fmt.Errorf("%s: %s", k, err) | ||
237 | } | ||
238 | case TypeInt: | ||
239 | var n int | ||
240 | if err := mapstructure.Decode(v, &n); err != nil { | ||
241 | return fmt.Errorf("%s: %s", k, err) | ||
242 | } | ||
243 | set = strconv.FormatInt(int64(n), 10) | ||
244 | case TypeFloat: | ||
245 | var n float64 | ||
246 | if err := mapstructure.Decode(v, &n); err != nil { | ||
247 | return fmt.Errorf("%s: %s", k, err) | ||
248 | } | ||
249 | set = strconv.FormatFloat(float64(n), 'G', -1, 64) | ||
250 | default: | ||
251 | return fmt.Errorf("Unknown type: %#v", schema.Type) | ||
252 | } | ||
253 | |||
254 | w.result[k] = set | ||
255 | return nil | ||
256 | } | ||
257 | |||
258 | func (w *MapFieldWriter) setSet( | ||
259 | addr []string, | ||
260 | value interface{}, | ||
261 | schema *Schema) error { | ||
262 | addrCopy := make([]string, len(addr), len(addr)+1) | ||
263 | copy(addrCopy, addr) | ||
264 | k := strings.Join(addr, ".") | ||
265 | |||
266 | if value == nil { | ||
267 | w.result[k+".#"] = "0" | ||
268 | return nil | ||
269 | } | ||
270 | |||
271 | // If it is a slice, then we have to turn it into a *Set so that | ||
272 | // we get the proper order back based on the hash code. | ||
273 | if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { | ||
274 | // Build a temp *ResourceData to use for the conversion | ||
275 | tempSchema := *schema | ||
276 | tempSchema.Type = TypeList | ||
277 | tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema} | ||
278 | tempW := &MapFieldWriter{Schema: tempSchemaMap} | ||
279 | |||
280 | // Set the entire list, this lets us get sane values out of it | ||
281 | if err := tempW.WriteField(addr, value); err != nil { | ||
282 | return err | ||
283 | } | ||
284 | |||
285 | // Build the set by going over the list items in order and | ||
286 | // hashing them into the set. The reason we go over the list and | ||
287 | // not the `value` directly is because this forces all types | ||
288 | // to become []interface{} (generic) instead of []string, which | ||
289 | // most hash functions are expecting. | ||
290 | s := schema.ZeroValue().(*Set) | ||
291 | tempR := &MapFieldReader{ | ||
292 | Map: BasicMapReader(tempW.Map()), | ||
293 | Schema: tempSchemaMap, | ||
294 | } | ||
295 | for i := 0; i < v.Len(); i++ { | ||
296 | is := strconv.FormatInt(int64(i), 10) | ||
297 | result, err := tempR.ReadField(append(addrCopy, is)) | ||
298 | if err != nil { | ||
299 | return err | ||
300 | } | ||
301 | if !result.Exists { | ||
302 | panic("set item just set doesn't exist") | ||
303 | } | ||
304 | |||
305 | s.Add(result.Value) | ||
306 | } | ||
307 | |||
308 | value = s | ||
309 | } | ||
310 | |||
311 | for code, elem := range value.(*Set).m { | ||
312 | if err := w.set(append(addrCopy, code), elem); err != nil { | ||
313 | return err | ||
314 | } | ||
315 | } | ||
316 | |||
317 | w.result[k+".#"] = strconv.Itoa(value.(*Set).Len()) | ||
318 | return nil | ||
319 | } | ||