From c3f1f49640abfcde7ced4d039f3a83a77ce4df03 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 17 Aug 2014 14:12:54 -0700 Subject: [PATCH] helper/schema: final state for lists/objects works --- helper/schema/resource_data.go | 90 ++++++++++++++ helper/schema/resource_data_test.go | 181 ++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index f2a7cb072..abb1c3114 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -44,6 +44,14 @@ func (d *ResourceData) Set(key string, value interface{}) error { return d.setObject("", parts, d.schema, value) } +// State returns the new ResourceState after the diff and any Set +// calls. +func (d *ResourceData) State() *terraform.ResourceState { + var result terraform.ResourceState + result.Attributes = d.stateObject("", d.schema) + return &result +} + func (d *ResourceData) get( k string, parts []string, @@ -316,3 +324,85 @@ func (d *ResourceData) setPrimitive( d.setMap[k] = set return nil } + +func (d *ResourceData) stateList( + prefix string, + schema *Schema) map[string]string { + countRaw := d.get(prefix, []string{"#"}, schema) + if countRaw == nil { + return nil + } + count := countRaw.(int) + + result := make(map[string]string) + result[prefix + ".#"] = strconv.FormatInt(int64(count), 10) + for i := 0; i < count; i++ { + key := fmt.Sprintf("%s.%d", prefix, i) + + var m map[string]string + switch t := schema.Elem.(type) { + case *Resource: + m = d.stateObject(key, t.Schema) + case *Schema: + m = d.stateSingle(key, t) + } + + for k, v := range m { + result[k] = v + } + } + + return result +} + +func (d *ResourceData) stateObject( + prefix string, + schema map[string]*Schema) map[string]string { + result := make(map[string]string) + for k, v := range schema { + key := k + if prefix != "" { + key = prefix + "." + key + } + + for k1, v1 := range d.stateSingle(key, v) { + result[k1] = v1 + } + } + + return result +} + +func (d *ResourceData) statePrimitive( + prefix string, + schema *Schema) map[string]string { + v := d.getPrimitive(prefix, nil, schema) + if v == nil { + return nil + } + + var vs string + switch schema.Type { + case TypeString: + vs = v.(string) + case TypeInt: + vs = strconv.FormatInt(int64(v.(int)), 10) + default: + panic(fmt.Sprintf("Unknown type: %s", schema.Type)) + } + + return map[string]string{ + prefix: vs, + } +} + +func (d *ResourceData) stateSingle( + prefix string, + schema *Schema) map[string]string { + switch schema.Type { + case TypeList: + return d.stateList(prefix, schema) + default: + return d.statePrimitive(prefix, schema) + } +} diff --git a/helper/schema/resource_data_test.go b/helper/schema/resource_data_test.go index 1df5fb137..c5b818fee 100644 --- a/helper/schema/resource_data_test.go +++ b/helper/schema/resource_data_test.go @@ -615,3 +615,184 @@ func TestResourceDataSet(t *testing.T) { } } } + +func TestResourceDataState(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.ResourceState + Diff *terraform.ResourceDiff + Set map[string]interface{} + Result *terraform.ResourceState + }{ + // Basic primitive in diff + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Result: &terraform.ResourceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + }, + + // Basic primitive set override + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Set: map[string]interface{}{ + "availability_zone": "bar", + }, + + Result: &terraform.ResourceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + }, + }, + }, + + // List + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.ResourceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0": "80", + }, + }, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.ResourceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "80", + "ports.1": "100", + }, + }, + }, + + // List of resources + { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: &terraform.ResourceState{ + Attributes: map[string]string{ + "ingress.#": "1", + "ingress.0.from": "80", + }, + }, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "80", + New: "150", + }, + "ingress.1.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.ResourceState{ + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.0.from": "150", + "ingress.1.from": "100", + }, + }, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + for k, v := range tc.Set { + if err := d.Set(k, v); err != nil { + t.Fatalf("%d err: %s", i, err) + } + } + + actual := d.State() + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("Bad: %d\n\n%#v", i, actual) + } + } +}