diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 8e0ef4b7b3..22876c5166 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -777,7 +777,7 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro } if len(v.RequiredWith) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.RequiredWith, topSchemaMap, v, false) + err := checkKeysAgainstSchemaFlags(k, v.RequiredWith, topSchemaMap, v, true) if err != nil { return fmt.Errorf("RequiredWith: %+v", err) } @@ -860,9 +860,7 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro return nil } -func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap, self *Schema, requireSelfReference bool) error { - var foundSelfReference bool - +func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap, self *Schema, allowSelfReference bool) error { for _, key := range keys { parts := strings.Split(key, ".") sm := topSchemaMap @@ -901,12 +899,8 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap return fmt.Errorf("%s cannot find target attribute (%s), sm: %#v", k, key, sm) } - if target == self { - if !requireSelfReference { - return fmt.Errorf("%s cannot reference self (%s)", k, key) - } - - foundSelfReference = true + if target == self && !allowSelfReference { + return fmt.Errorf("%s cannot reference self (%s)", k, key) } if target.Required { @@ -918,10 +912,6 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap } } - if requireSelfReference && !foundSelfReference { - return fmt.Errorf("%s must reference self for AtLeastOneOf/ExactlyOneOf", k) - } - return nil } diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 81c0914313..dc25b1bd8e 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -3636,7 +3636,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, }, }, - true, + false, }, "AtLeastOneOf list index syntax with list configuration block missing attribute": { @@ -3844,7 +3844,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"map_attr"}, }, }, - true, + false, }, "AtLeastOneOf string syntax with self reference": { @@ -4161,7 +4161,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, }, }, - true, + false, }, "ExactlyOneOf list index syntax with list configuration block missing attribute": { @@ -4369,7 +4369,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { ExactlyOneOf: []string{"map_attr"}, }, }, - true, + false, }, "ExactlyOneOf string syntax with self reference": { @@ -4383,6 +4383,269 @@ func TestSchemaMap_InternalValidate(t *testing.T) { false, }, + "RequiredWith list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + false, + }, + + "RequiredWith list index syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, + }, + + "RequiredWith list index syntax with list configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with list configuration block missing MaxItems": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with set configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with list configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "RequiredWith map key syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with set configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "RequiredWith map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "RequiredWith string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"map_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"test"}, + }, + }, + false, + }, + "Sub-resource invalid": { map[string]*Schema{ "foo": {