Skip to content

Commit 06d30a5

Browse files
committed
helper/schema: Set object
1 parent 2d74a3c commit 06d30a5

2 files changed

Lines changed: 301 additions & 8 deletions

File tree

helper/schema/resource_data.go

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type ResourceData struct {
1414
schema map[string]*Schema
1515
state *terraform.ResourceState
1616
diff *terraform.ResourceDiff
17-
set map[string]string
17+
setMap map[string]string
1818
}
1919

2020
// Get returns the data for the given key, or nil if the key doesn't exist.
@@ -36,8 +36,8 @@ func (d *ResourceData) Get(key string) interface{} {
3636
// If the key is invalid or the value is not a correct type, an error
3737
// will be returned.
3838
func (d *ResourceData) Set(key string, value interface{}) error {
39-
if d.set == nil {
40-
d.set = make(map[string]string)
39+
if d.setMap == nil {
40+
d.setMap = make(map[string]string)
4141
}
4242

4343
parts := strings.Split(key, ".")
@@ -145,8 +145,8 @@ func (d *ResourceData) getPrimitive(
145145
}
146146
}
147147

148-
if d.set != nil {
149-
if v, ok := d.set[k]; ok {
148+
if d.setMap != nil {
149+
if v, ok := d.setMap[k]; ok {
150150
result = v
151151
resultSet = true
152152
}
@@ -176,6 +176,70 @@ func (d *ResourceData) getPrimitive(
176176
}
177177
}
178178

179+
func (d *ResourceData) set(
180+
k string,
181+
parts []string,
182+
schema *Schema,
183+
value interface{}) error {
184+
switch schema.Type {
185+
case TypeList:
186+
return d.setList(k, parts, schema, value)
187+
default:
188+
return d.setPrimitive(k, schema, value)
189+
}
190+
}
191+
192+
func (d *ResourceData) setList(
193+
k string,
194+
parts []string,
195+
schema *Schema,
196+
value interface{}) error {
197+
if len(parts) > 0 {
198+
// We're setting a specific element
199+
idx := parts[0]
200+
parts = parts[1:]
201+
202+
// Special case if we're accessing the count of the list
203+
if idx == "#" {
204+
return fmt.Errorf("%s: can't set count of list", k)
205+
}
206+
207+
key := fmt.Sprintf("%s.%s", k, idx)
208+
switch t := schema.Elem.(type) {
209+
case *Resource:
210+
return d.setObject(key, parts, t.Schema, value)
211+
case *Schema:
212+
return d.set(key, parts, t, value)
213+
}
214+
}
215+
216+
var vs []interface{}
217+
if err := mapstructure.Decode(value, &vs); err != nil {
218+
return fmt.Errorf("%s: %s", k, err)
219+
}
220+
221+
// Set the entire list.
222+
var err error
223+
for i, elem := range vs {
224+
is := strconv.FormatInt(int64(i), 10)
225+
err = d.setList(k, []string{is}, schema, elem)
226+
if err != nil {
227+
break
228+
}
229+
}
230+
if err != nil {
231+
for i, _ := range vs {
232+
is := strconv.FormatInt(int64(i), 10)
233+
d.setList(k, []string{is}, schema, nil)
234+
}
235+
236+
return err
237+
}
238+
239+
d.setMap[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
240+
return nil
241+
}
242+
179243
func (d *ResourceData) setObject(
180244
k string,
181245
parts []string,
@@ -197,16 +261,41 @@ func (d *ResourceData) setObject(
197261
key = fmt.Sprintf("%s.%s", k, key)
198262
}
199263

200-
return d.setPrimitive(key, s, value)
264+
return d.set(key, parts, s, value)
265+
}
266+
267+
// Set the entire object. First decode into a proper structure
268+
var v map[string]interface{}
269+
if err := mapstructure.Decode(value, &v); err != nil {
270+
return fmt.Errorf("%s: %s", k, err)
201271
}
202272

203-
panic("can't set full object yet")
273+
// Set each element in turn
274+
var err error
275+
for k1, v1 := range v {
276+
err = d.setObject(k, []string{k1}, schema, v1)
277+
if err != nil {
278+
break
279+
}
280+
}
281+
if err != nil {
282+
for k1, _ := range v {
283+
d.setObject(k, []string{k1}, schema, nil)
284+
}
285+
}
286+
287+
return err
204288
}
205289

206290
func (d *ResourceData) setPrimitive(
207291
k string,
208292
schema *Schema,
209293
v interface{}) error {
294+
if v == nil {
295+
delete(d.setMap, k)
296+
return nil
297+
}
298+
210299
var set string
211300
switch schema.Type {
212301
case TypeString:
@@ -224,6 +313,6 @@ func (d *ResourceData) setPrimitive(
224313
return fmt.Errorf("Unknown type: %s", schema.Type)
225314
}
226315

227-
d.set[k] = set
316+
d.setMap[k] = set
228317
return nil
229318
}

helper/schema/resource_data_test.go

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,210 @@ func TestResourceDataSet(t *testing.T) {
392392
GetKey: "availability_zone",
393393
GetValue: nil,
394394
},
395+
396+
// List of primitives, set element
397+
{
398+
Schema: map[string]*Schema{
399+
"ports": &Schema{
400+
Type: TypeList,
401+
Computed: true,
402+
Elem: &Schema{Type: TypeInt},
403+
},
404+
},
405+
406+
State: &terraform.ResourceState{
407+
Attributes: map[string]string{
408+
"ports.#": "3",
409+
"ports.0": "1",
410+
"ports.1": "2",
411+
"ports.2": "5",
412+
},
413+
},
414+
415+
Diff: nil,
416+
417+
Key: "ports.1",
418+
Value: 3,
419+
420+
GetKey: "ports",
421+
GetValue: []interface{}{1, 3, 5},
422+
},
423+
424+
// List of primitives, set list
425+
{
426+
Schema: map[string]*Schema{
427+
"ports": &Schema{
428+
Type: TypeList,
429+
Computed: true,
430+
Elem: &Schema{Type: TypeInt},
431+
},
432+
},
433+
434+
State: nil,
435+
436+
Diff: nil,
437+
438+
Key: "ports",
439+
Value: []int{1, 2, 5},
440+
441+
GetKey: "ports",
442+
GetValue: []interface{}{1, 2, 5},
443+
},
444+
445+
// List of primitives, set list with error
446+
{
447+
Schema: map[string]*Schema{
448+
"ports": &Schema{
449+
Type: TypeList,
450+
Computed: true,
451+
Elem: &Schema{Type: TypeInt},
452+
},
453+
},
454+
455+
State: nil,
456+
457+
Diff: nil,
458+
459+
Key: "ports",
460+
Value: []interface{}{1, "NOPE", 5},
461+
Err: true,
462+
463+
GetKey: "ports",
464+
GetValue: []interface{}{},
465+
},
466+
467+
// List of resource, set element
468+
{
469+
Schema: map[string]*Schema{
470+
"ingress": &Schema{
471+
Type: TypeList,
472+
Computed: true,
473+
Elem: &Resource{
474+
Schema: map[string]*Schema{
475+
"from": &Schema{
476+
Type: TypeInt,
477+
},
478+
},
479+
},
480+
},
481+
},
482+
483+
State: &terraform.ResourceState{
484+
Attributes: map[string]string{
485+
"ingress.#": "2",
486+
"ingress.0.from": "80",
487+
"ingress.1.from": "8080",
488+
},
489+
},
490+
491+
Diff: nil,
492+
493+
Key: "ingress.1.from",
494+
Value: 9000,
495+
496+
GetKey: "ingress",
497+
GetValue: []interface{}{
498+
map[string]interface{}{
499+
"from": 80,
500+
},
501+
map[string]interface{}{
502+
"from": 9000,
503+
},
504+
},
505+
},
506+
507+
// List of resource, set full resource element
508+
{
509+
Schema: map[string]*Schema{
510+
"ingress": &Schema{
511+
Type: TypeList,
512+
Computed: true,
513+
Elem: &Resource{
514+
Schema: map[string]*Schema{
515+
"from": &Schema{
516+
Type: TypeInt,
517+
},
518+
},
519+
},
520+
},
521+
},
522+
523+
State: &terraform.ResourceState{
524+
Attributes: map[string]string{
525+
"ingress.#": "2",
526+
"ingress.0.from": "80",
527+
"ingress.1.from": "8080",
528+
},
529+
},
530+
531+
Diff: nil,
532+
533+
Key: "ingress.1",
534+
Value: map[string]interface{}{
535+
"from": 9000,
536+
},
537+
538+
GetKey: "ingress",
539+
GetValue: []interface{}{
540+
map[string]interface{}{
541+
"from": 80,
542+
},
543+
map[string]interface{}{
544+
"from": 9000,
545+
},
546+
},
547+
},
548+
549+
// List of resource, set full resource element, with error
550+
{
551+
Schema: map[string]*Schema{
552+
"ingress": &Schema{
553+
Type: TypeList,
554+
Computed: true,
555+
Elem: &Resource{
556+
Schema: map[string]*Schema{
557+
"from": &Schema{
558+
Type: TypeInt,
559+
},
560+
"to": &Schema{
561+
Type: TypeInt,
562+
},
563+
},
564+
},
565+
},
566+
},
567+
568+
State: &terraform.ResourceState{
569+
Attributes: map[string]string{
570+
"ingress.#": "2",
571+
"ingress.0.from": "80",
572+
"ingress.0.to": "10",
573+
"ingress.1.from": "8080",
574+
"ingress.1.to": "8080",
575+
},
576+
},
577+
578+
Diff: nil,
579+
580+
Key: "ingress.1",
581+
Value: map[string]interface{}{
582+
"from": 9000,
583+
"to": "bar",
584+
},
585+
Err: true,
586+
587+
GetKey: "ingress",
588+
GetValue: []interface{}{
589+
map[string]interface{}{
590+
"from": 80,
591+
"to": 10,
592+
},
593+
map[string]interface{}{
594+
"from": 8080,
595+
"to": 8080,
596+
},
597+
},
598+
},
395599
}
396600

397601
for i, tc := range cases {

0 commit comments

Comments
 (0)