Skip to content

Commit c3f1f49

Browse files
committed
helper/schema: final state for lists/objects works
1 parent 06d30a5 commit c3f1f49

2 files changed

Lines changed: 271 additions & 0 deletions

File tree

helper/schema/resource_data.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ func (d *ResourceData) Set(key string, value interface{}) error {
4444
return d.setObject("", parts, d.schema, value)
4545
}
4646

47+
// State returns the new ResourceState after the diff and any Set
48+
// calls.
49+
func (d *ResourceData) State() *terraform.ResourceState {
50+
var result terraform.ResourceState
51+
result.Attributes = d.stateObject("", d.schema)
52+
return &result
53+
}
54+
4755
func (d *ResourceData) get(
4856
k string,
4957
parts []string,
@@ -316,3 +324,85 @@ func (d *ResourceData) setPrimitive(
316324
d.setMap[k] = set
317325
return nil
318326
}
327+
328+
func (d *ResourceData) stateList(
329+
prefix string,
330+
schema *Schema) map[string]string {
331+
countRaw := d.get(prefix, []string{"#"}, schema)
332+
if countRaw == nil {
333+
return nil
334+
}
335+
count := countRaw.(int)
336+
337+
result := make(map[string]string)
338+
result[prefix + ".#"] = strconv.FormatInt(int64(count), 10)
339+
for i := 0; i < count; i++ {
340+
key := fmt.Sprintf("%s.%d", prefix, i)
341+
342+
var m map[string]string
343+
switch t := schema.Elem.(type) {
344+
case *Resource:
345+
m = d.stateObject(key, t.Schema)
346+
case *Schema:
347+
m = d.stateSingle(key, t)
348+
}
349+
350+
for k, v := range m {
351+
result[k] = v
352+
}
353+
}
354+
355+
return result
356+
}
357+
358+
func (d *ResourceData) stateObject(
359+
prefix string,
360+
schema map[string]*Schema) map[string]string {
361+
result := make(map[string]string)
362+
for k, v := range schema {
363+
key := k
364+
if prefix != "" {
365+
key = prefix + "." + key
366+
}
367+
368+
for k1, v1 := range d.stateSingle(key, v) {
369+
result[k1] = v1
370+
}
371+
}
372+
373+
return result
374+
}
375+
376+
func (d *ResourceData) statePrimitive(
377+
prefix string,
378+
schema *Schema) map[string]string {
379+
v := d.getPrimitive(prefix, nil, schema)
380+
if v == nil {
381+
return nil
382+
}
383+
384+
var vs string
385+
switch schema.Type {
386+
case TypeString:
387+
vs = v.(string)
388+
case TypeInt:
389+
vs = strconv.FormatInt(int64(v.(int)), 10)
390+
default:
391+
panic(fmt.Sprintf("Unknown type: %s", schema.Type))
392+
}
393+
394+
return map[string]string{
395+
prefix: vs,
396+
}
397+
}
398+
399+
func (d *ResourceData) stateSingle(
400+
prefix string,
401+
schema *Schema) map[string]string {
402+
switch schema.Type {
403+
case TypeList:
404+
return d.stateList(prefix, schema)
405+
default:
406+
return d.statePrimitive(prefix, schema)
407+
}
408+
}

helper/schema/resource_data_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,184 @@ func TestResourceDataSet(t *testing.T) {
615615
}
616616
}
617617
}
618+
619+
func TestResourceDataState(t *testing.T) {
620+
cases := []struct {
621+
Schema map[string]*Schema
622+
State *terraform.ResourceState
623+
Diff *terraform.ResourceDiff
624+
Set map[string]interface{}
625+
Result *terraform.ResourceState
626+
}{
627+
// Basic primitive in diff
628+
{
629+
Schema: map[string]*Schema{
630+
"availability_zone": &Schema{
631+
Type: TypeString,
632+
Optional: true,
633+
Computed: true,
634+
ForceNew: true,
635+
},
636+
},
637+
638+
State: nil,
639+
640+
Diff: &terraform.ResourceDiff{
641+
Attributes: map[string]*terraform.ResourceAttrDiff{
642+
"availability_zone": &terraform.ResourceAttrDiff{
643+
Old: "",
644+
New: "foo",
645+
RequiresNew: true,
646+
},
647+
},
648+
},
649+
650+
Result: &terraform.ResourceState{
651+
Attributes: map[string]string{
652+
"availability_zone": "foo",
653+
},
654+
},
655+
},
656+
657+
// Basic primitive set override
658+
{
659+
Schema: map[string]*Schema{
660+
"availability_zone": &Schema{
661+
Type: TypeString,
662+
Optional: true,
663+
Computed: true,
664+
ForceNew: true,
665+
},
666+
},
667+
668+
State: nil,
669+
670+
Diff: &terraform.ResourceDiff{
671+
Attributes: map[string]*terraform.ResourceAttrDiff{
672+
"availability_zone": &terraform.ResourceAttrDiff{
673+
Old: "",
674+
New: "foo",
675+
RequiresNew: true,
676+
},
677+
},
678+
},
679+
680+
Set: map[string]interface{}{
681+
"availability_zone": "bar",
682+
},
683+
684+
Result: &terraform.ResourceState{
685+
Attributes: map[string]string{
686+
"availability_zone": "bar",
687+
},
688+
},
689+
},
690+
691+
// List
692+
{
693+
Schema: map[string]*Schema{
694+
"ports": &Schema{
695+
Type: TypeList,
696+
Required: true,
697+
Elem: &Schema{Type: TypeInt},
698+
},
699+
},
700+
701+
State: &terraform.ResourceState{
702+
Attributes: map[string]string{
703+
"ports.#": "1",
704+
"ports.0": "80",
705+
},
706+
},
707+
708+
Diff: &terraform.ResourceDiff{
709+
Attributes: map[string]*terraform.ResourceAttrDiff{
710+
"ports.#": &terraform.ResourceAttrDiff{
711+
Old: "1",
712+
New: "2",
713+
},
714+
"ports.1": &terraform.ResourceAttrDiff{
715+
Old: "",
716+
New: "100",
717+
},
718+
},
719+
},
720+
721+
Result: &terraform.ResourceState{
722+
Attributes: map[string]string{
723+
"ports.#": "2",
724+
"ports.0": "80",
725+
"ports.1": "100",
726+
},
727+
},
728+
},
729+
730+
// List of resources
731+
{
732+
Schema: map[string]*Schema{
733+
"ingress": &Schema{
734+
Type: TypeList,
735+
Required: true,
736+
Elem: &Resource{
737+
Schema: map[string]*Schema{
738+
"from": &Schema{
739+
Type: TypeInt,
740+
Required: true,
741+
},
742+
},
743+
},
744+
},
745+
},
746+
747+
State: &terraform.ResourceState{
748+
Attributes: map[string]string{
749+
"ingress.#": "1",
750+
"ingress.0.from": "80",
751+
},
752+
},
753+
754+
Diff: &terraform.ResourceDiff{
755+
Attributes: map[string]*terraform.ResourceAttrDiff{
756+
"ingress.#": &terraform.ResourceAttrDiff{
757+
Old: "1",
758+
New: "2",
759+
},
760+
"ingress.0.from": &terraform.ResourceAttrDiff{
761+
Old: "80",
762+
New: "150",
763+
},
764+
"ingress.1.from": &terraform.ResourceAttrDiff{
765+
Old: "",
766+
New: "100",
767+
},
768+
},
769+
},
770+
771+
Result: &terraform.ResourceState{
772+
Attributes: map[string]string{
773+
"ingress.#": "2",
774+
"ingress.0.from": "150",
775+
"ingress.1.from": "100",
776+
},
777+
},
778+
},
779+
}
780+
781+
for i, tc := range cases {
782+
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
783+
if err != nil {
784+
t.Fatalf("err: %s", err)
785+
}
786+
787+
for k, v := range tc.Set {
788+
if err := d.Set(k, v); err != nil {
789+
t.Fatalf("%d err: %s", i, err)
790+
}
791+
}
792+
793+
actual := d.State()
794+
if !reflect.DeepEqual(actual, tc.Result) {
795+
t.Fatalf("Bad: %d\n\n%#v", i, actual)
796+
}
797+
}
798+
}

0 commit comments

Comments
 (0)