diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index ceffd46b1..615038d2d 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -77,6 +77,18 @@ func (d *ResourceData) GetChange(key string) (interface{}, interface{}) { func (d *ResourceData) GetOk(key string) (interface{}, bool) { r := d.getRaw(key, getSourceSet) exists := r.Exists && !r.Computed + if exists { + // If it exists, we also want to verify it is not the zero-value. + value := r.Value + zero := r.Schema.Type.Zero() + + if eq, ok := value.(Equal); ok { + exists = !eq.Equal(zero) + } else { + exists = !reflect.DeepEqual(value, zero) + } + } + return r.Value, exists } diff --git a/helper/schema/resource_data_test.go b/helper/schema/resource_data_test.go index 1f484ff4f..9e35680da 100644 --- a/helper/schema/resource_data_test.go +++ b/helper/schema/resource_data_test.go @@ -828,7 +828,7 @@ func TestResourceDataGetOk(t *testing.T) { Key: "availability_zone", Value: "", - Ok: true, + Ok: false, }, { @@ -961,6 +961,32 @@ func TestResourceDataGetOk(t *testing.T) { Value: 0, Ok: false, }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "0", + }, + }, + }, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, } for i, tc := range cases { diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 878901418..ff989069c 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1084,3 +1084,29 @@ func (m schemaMap) validateType( return m.validatePrimitive(k, raw, schema, c) } } + +// Zero returns the zero value for a type. +func (t ValueType) Zero() interface{} { + switch t { + case TypeInvalid: + return nil + case TypeBool: + return false + case TypeInt: + return 0 + case TypeFloat: + return 0.0 + case TypeString: + return "" + case TypeList: + return []interface{}{} + case TypeMap: + return map[string]interface{}{} + case TypeSet: + return new(Set) + case typeObject: + return map[string]interface{}{} + default: + panic(fmt.Sprintf("unknown type %s", t)) + } +} diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 8d47d6af9..2f33a3ec2 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -111,7 +111,7 @@ func TestValueType_Zero(t *testing.T) { {TypeString, ""}, {TypeList, []interface{}{}}, {TypeMap, map[string]interface{}{}}, - {TypeSet, nil}, + {TypeSet, new(Set)}, } for i, tc := range cases { diff --git a/helper/schema/valuetype.go b/helper/schema/valuetype.go index b7b7ac810..9286987d5 100644 --- a/helper/schema/valuetype.go +++ b/helper/schema/valuetype.go @@ -2,8 +2,6 @@ package schema //go:generate stringer -type=ValueType valuetype.go -import "fmt" - // ValueType is an enum of the type that can be represented by a schema. type ValueType int @@ -19,28 +17,5 @@ const ( typeObject ) -// Zero returns the zero value for a type. -func (t ValueType) Zero() interface{} { - switch t { - case TypeInvalid: - return nil - case TypeBool: - return false - case TypeInt: - return 0 - case TypeFloat: - return 0.0 - case TypeString: - return "" - case TypeList: - return []interface{}{} - case TypeMap: - return map[string]interface{}{} - case TypeSet: - return nil - case typeObject: - return map[string]interface{}{} - default: - panic(fmt.Sprintf("unknown type %s", t)) - } -} +// NOTE: ValueType has more functions defined on it in schema.go. We can't +// put them here because we reference other files.