From e45debe0e5728851910f13926b3c55d67dac693a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 25 Oct 2016 20:56:45 -0400 Subject: [PATCH] helper/schema: only mark "ForceNew" on resources that cause the ForceNew Fixes #2748 This changes the diff to only mark "forces new resource" on the fields that actually caused the new resource, not every field that changed. This makes diffs much more accurate. I'd like to request a review but I'm going to defer merging until Terraform 0.8. Changes like this are very possible to cause "diffs didn't match" errors and I want some real world testing in a beta before we hit prod with this. --- helper/schema/schema.go | 16 +++++- helper/schema/schema_test.go | 94 ++++++++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index b6a16fdbdb..4a2fd17146 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -288,9 +288,21 @@ func (s *Schema) finalizeDiff( d.New = normalizeBoolString(d.New) } + if s.Computed && !d.NewRemoved && d.New == "" { + // Computed attribute without a new value set + d.NewComputed = true + } + if s.ForceNew { - // Force new, set it to true in the diff - d.RequiresNew = true + // ForceNew, mark that this field is requiring new under the + // following conditions, explained below: + // + // * Old != New - There is a change in value. This field + // is therefore causing a new resource. + // + // * NewComputed - This field is being computed, hence a + // potential change in value, mark as causing a new resource. + d.RequiresNew = d.Old != d.New || d.NewComputed } if d.NewRemoved { diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 35c911de9d..a68d8ac89b 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -2341,9 +2341,8 @@ func TestSchemaMap_Diff(t *testing.T) { RequiresNew: true, }, "instances.3": &terraform.ResourceAttrDiff{ - Old: "333", - New: "333", - RequiresNew: true, + Old: "333", + New: "333", }, "instances.4": &terraform.ResourceAttrDiff{ Old: "", @@ -2426,6 +2425,61 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + "Set ForceNew only marks the changing element as ForceNew": { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "3", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": &terraform.ResourceAttrDiff{ + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + "removed optional items should trigger ForceNew": { Schema: map[string]*Schema{ "description": &Schema{ @@ -2495,26 +2549,28 @@ func TestSchemaMap_Diff(t *testing.T) { } for tn, tc := range cases { - c, err := config.NewRawConfig(tc.Config) - if err != nil { - t.Fatalf("#%q err: %s", tn, err) - } - - if len(tc.ConfigVariables) > 0 { - if err := c.Interpolate(tc.ConfigVariables); err != nil { + t.Run(tn, func(t *testing.T) { + c, err := config.NewRawConfig(tc.Config) + if err != nil { t.Fatalf("#%q err: %s", tn, err) } - } - d, err := schemaMap(tc.Schema).Diff( - tc.State, terraform.NewResourceConfig(c)) - if err != nil != tc.Err { - t.Fatalf("#%q err: %s", tn, err) - } + if len(tc.ConfigVariables) > 0 { + if err := c.Interpolate(tc.ConfigVariables); err != nil { + t.Fatalf("#%q err: %s", tn, err) + } + } - if !reflect.DeepEqual(tc.Diff, d) { - t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Diff, d) - } + d, err := schemaMap(tc.Schema).Diff( + tc.State, terraform.NewResourceConfig(c)) + if err != nil != tc.Err { + t.Fatalf("#%q err: %s", tn, err) + } + + if !reflect.DeepEqual(tc.Diff, d) { + t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Diff, d) + } + }) } }