diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index d12e754604..7fbc49caca 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/go-cty/cty/gocty" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -155,14 +156,7 @@ func (d *ResourceData) HasChangesExcept(keys ...string) bool { func (d *ResourceData) HasChange(key string) bool { o, n := d.GetChange(key) - // If the type implements the Equal interface, then call that - // instead of just doing a reflect.DeepEqual. An example where this is - // needed is *Set - if eq, ok := o.(Equal); ok { - return !eq.Equal(n) - } - - return !reflect.DeepEqual(o, n) + return !cmp.Equal(n, o) } // HasChangeExcept returns whether any keys outside the given key have been changed. diff --git a/helper/schema/resource_data_test.go b/helper/schema/resource_data_test.go index d3e06fa0ea..bc0222fd67 100644 --- a/helper/schema/resource_data_test.go +++ b/helper/schema/resource_data_test.go @@ -2075,6 +2075,87 @@ func TestResourceDataHasChange(t *testing.T) { Change: false, }, + + { + Schema: map[string]*Schema{ + "network_configuration": { + Type: TypeList, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "security_groups": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "network_configuration.#": "1", + "network_configuration.0.security_groups.#": "2", + "network_configuration.0.security_groups.1268622331": "sg2", + "network_configuration.0.security_groups.3532976705": "sg1", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "network_configuration", + + Change: false, + }, + + { + Schema: map[string]*Schema{ + "network_configuration": { + Type: TypeList, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "security_groups": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "network_configuration.#": "1", + "network_configuration.0.security_groups.#": "2", + "network_configuration.0.security_groups.1268622331": "sg2", + "network_configuration.0.security_groups.3532976705": "sg1", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "network_configuration.0.security_groups.1268622331": { + Old: "sg2", + New: "", + }, + "network_configuration.0.security_groups.1016763245": { + Old: "", + New: "sg3", + }, + }, + }, + + Key: "network_configuration", + + Change: true, + }, } for i, tc := range cases { diff --git a/helper/schema/set.go b/helper/schema/set.go index a510e60ff0..48755c22d1 100644 --- a/helper/schema/set.go +++ b/helper/schema/set.go @@ -3,6 +3,7 @@ package schema import ( "bytes" "fmt" + "github.com/google/go-cmp/cmp" "reflect" "sort" "strconv" @@ -150,46 +151,12 @@ func (s *Set) Union(other *Set) *Set { return result } -func checkSetMapEqual(m1, m2 map[string]interface{}) bool { - if (m1 == nil) != (m2 == nil) { - return false - } - if len(m1) != len(m2) { - return false - } - for k := range m1 { - v1 := m1[k] - v2, ok := m2[k] - if !ok { - return false - } - switch v1.(type) { - case map[string]interface{}: - same := checkSetMapEqual(v1.(map[string]interface{}), v2.(map[string]interface{})) - if !same { - return false - } - case *Set: - same := v1.(*Set).Equal(v2) - if !same { - return false - } - default: - same := reflect.DeepEqual(v1, v2) - if !same { - return false - } - } - } - return true -} - func (s *Set) Equal(raw interface{}) bool { other, ok := raw.(*Set) if !ok { return false } - return checkSetMapEqual(s.m, other.m) + return cmp.Equal(s.m, other.m) } // HashEqual simply checks to the keys the top-level map to the keys in the