From e5877543b23f892887c17e2dd4294546b8660a17 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 15 Dec 2014 17:35:16 -0800 Subject: [PATCH] helper/schema: track map element counts This adds "field.#" values to the state/diff with the element count of a map. This fixes a major issue around not knowing when child elements are computed when doing variable access of a computed map. Example, if you have a schema like this: "foo": &Schema{ Type: TypeMap, Computed: true, } And you access it like this in a resource: ${type.name.foo.computed-field} Then Terraform will error that "field foo could not be found on resource type.name". By adding that "foo.#" is computed, Terraform core will pick up that it WILL exist, so its okay. --- helper/schema/schema.go | 44 +++++++++++++++++++++++++++++++++++- helper/schema/schema_test.go | 34 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 050ce11ed..b85f1c19a 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -575,7 +575,7 @@ func (m schemaMap) diffMap( // First get all the values from the state var stateMap, configMap map[string]string - o, n, _, _ := d.diffChange(k) + o, n, _, computedMap := d.diffChange(k) if err := mapstructure.WeakDecode(o, &stateMap); err != nil { return fmt.Errorf("%s: %s", k, err) } @@ -583,6 +583,48 @@ func (m schemaMap) diffMap( return fmt.Errorf("%s: %s", k, err) } + // Get the counts + oldLen, newLen := len(stateMap), len(configMap) + oldStr := strconv.FormatInt(int64(oldLen), 10) + + // If the whole map is computed, then just say the # is computed and exit. + if computedMap { + diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{ + Old: oldStr, + NewComputed: true, + } + return nil + } + + // Check if the number of elements has changed. If we're computing + // a list and there isn't a config, then it hasn't changed. + changed := oldLen != newLen + if oldLen != 0 && newLen == 0 && schema.Computed { + changed = false + } + computed := oldLen == 0 && newLen == 0 && schema.Computed + if changed || computed { + countSchema := &Schema{ + Type: TypeInt, + Computed: schema.Computed, + ForceNew: schema.ForceNew, + } + + newStr := "" + if !computed { + newStr = strconv.FormatInt(int64(newLen), 10) + } else { + oldStr = "" + } + + diff.Attributes[k+".#"] = countSchema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: oldStr, + New: newStr, + }, + ) + } + // If the new map is nil and we're computed, then ignore it. if n == nil && schema.Computed { return nil diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 9aba38316..f902fa836 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -1205,6 +1205,11 @@ func TestSchemaMap_Diff(t *testing.T) { Diff: &terraform.InstanceDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "config_vars.bar": &terraform.ResourceAttrDiff{ Old: "", New: "baz", @@ -1381,6 +1386,10 @@ func TestSchemaMap_Diff(t *testing.T) { Old: "1", New: "0", }, + "config_vars.0.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + }, "config_vars.0.foo": &terraform.ResourceAttrDiff{ Old: "bar", NewRemoved: true, @@ -1663,6 +1672,31 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + + // #43 - Computed maps + { + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, } for i, tc := range cases {