diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 05d21c7ff..1cddb4016 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1164,16 +1164,35 @@ func (m schemaMap) validateConflictingAttributes( return nil } - for _, conflicting_key := range schema.ConflictsWith { - if value, ok := c.Get(conflicting_key); ok { + for _, conflictingKey := range schema.ConflictsWith { + resolvedConflictingKey := resolveConflictingKey(k, conflictingKey) + if value, ok := c.Get(resolvedConflictingKey); ok { return fmt.Errorf( - "%q: conflicts with %s (%#v)", k, conflicting_key, value) + "%q: conflicts with %s (%#v)", k, resolvedConflictingKey, value) } } return nil } +//The key we are validating for (k) contains the index for the current list/set item +//Example: list.2.field +//The conflicting key does not contain the index so we must resolve it and add it to the conflicting key +//Example: list.conflictingField needs to become list.2.conflictingField +func resolveConflictingKey(k string, conflictingKey string) string { + if conflictingFieldIndex := strings.LastIndex(conflictingKey, "."); conflictingFieldIndex != -1 { + if parentName := conflictingKey[:conflictingFieldIndex+1]; parentName != "" { + if strings.HasPrefix(strings.ToLower(k), strings.ToLower(parentName)) { + conflictingFieldName := conflictingKey[conflictingFieldIndex+1:] + fieldIndex := strings.LastIndex(k, ".") + return k[:fieldIndex+1] + conflictingFieldName + } + } + } + + return conflictingKey +} + func (m schemaMap) validateList( k string, raw interface{}, diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 4119b7ff5..f44bef7e1 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/terraform" + "strings" ) func TestEnvDefaultFunc(t *testing.T) { @@ -5018,6 +5019,31 @@ func TestSchemaSet_ValidateMinItems(t *testing.T) { } } +func TestSchema_ResolveConflictingKey(t *testing.T) { + cases := []struct { + Key string + ConflictingKey string + ExpectedResolvedKey string + }{ + {"Field", "ConflictField", "ConflictField"}, + {"Nested.Field", "Nested.ConflictField", "Nested.ConflictField"}, + {"Double.Nested.Field", "Double.Nested.ConflictField", "Double.Nested.ConflictField"}, + {"List.0.Field", "List.ConflictingField", "List.0.ConflictingField"}, + {"Nested.List.0.Field", "Nested.List.ConflictingField", "Nested.List.0.ConflictingField"}, + {"Field", ".G.I.G.O.", ".G.I.G.O."}, + {"Field", ".", "."}, + {"Field", "", ""}, + } + + for i, tc := range cases { + actualResolvedKey := resolveConflictingKey(tc.Key, tc.ConflictingKey) + + if !strings.EqualFold(actualResolvedKey, tc.ExpectedResolvedKey) { + t.Errorf("%d: expected: %s actual: %s", i, tc.ExpectedResolvedKey, actualResolvedKey) + } + } +} + // errorSort implements sort.Interface to sort errors by their error message type errorSort []error