From 52d7683e3f2d794843f91d3cd8a9458fe6348db7 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 29 Apr 2020 09:15:59 -0400 Subject: [PATCH 1/3] tests/helper/schema: Add unit tests for InternalValidate with AtLeastOneOf/ExactlyOneOf Reference: https://github.com/hashicorp/terraform-plugin-sdk/pull/407 Reference: https://github.com/hashicorp/terraform-plugin-sdk/pull/416 Includes TODO items for logic that needs adjustment to work as expected. `AtLeastOneOf` and `ExactlyOneOf` at the attribute level should not only allow self-reference, but also require it. --- helper/schema/schema_test.go | 524 +++++++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 9926067b5d..1bac8baf03 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -3310,6 +3310,268 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, + "AtLeastOneOf list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + true, // TODO: AtLeastOneOf self reference is necessary + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, // TODO: AtLeastOneOf self reference is necessary + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf 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, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "AtLeastOneOf string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"map_attr"}, + }, + }, + false, // TODO: AtLeastOneOf self reference is necessary + }, + + "AtLeastOneOf string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"test"}, + }, + }, + true, // TODO: AtLeastOneOf self reference is necessary + }, + "ConflictsWith list index syntax with self reference": { map[string]*Schema{ "config_block_attr": { @@ -3572,6 +3834,268 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, + "ExactlyOneOf list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + true, // TODO: ExactlyOneOf self reference is necessary + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, // TODO: ExactlyOneOf self reference is necessary + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf 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, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "ExactlyOneOf string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"map_attr"}, + }, + }, + false, // TODO: ExactlyOneOf self reference is necessary + }, + + "ExactlyOneOf string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"test"}, + }, + }, + true, // TODO: ExactlyOneOf self reference is necessary + }, + "Sub-resource invalid": { map[string]*Schema{ "foo": { From 2aac2e47ef56d56fc2308977b2ff8d84c132efa0 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 29 Apr 2020 09:24:37 -0400 Subject: [PATCH 2/3] helper/schema: Allow and require self references with AllowOneOf and ExactlyOneOf in InternalValidate Reference: https://github.com/hashicorp/terraform-plugin-sdk/pull/407 Reference: https://github.com/hashicorp/terraform-plugin-sdk/pull/416 --- helper/schema/schema.go | 23 +++++++++++++++++------ helper/schema/schema_test.go | 18 ++++++++++-------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 7e7d73d2c7..93a7a6a101 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -750,28 +750,28 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro } if len(v.ConflictsWith) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.ConflictsWith, topSchemaMap, v) + err := checkKeysAgainstSchemaFlags(k, v.ConflictsWith, topSchemaMap, v, false) if err != nil { return fmt.Errorf("ConflictsWith: %+v", err) } } if len(v.RequiredWith) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.RequiredWith, topSchemaMap, v) + err := checkKeysAgainstSchemaFlags(k, v.RequiredWith, topSchemaMap, v, false) if err != nil { return fmt.Errorf("RequiredWith: %+v", err) } } if len(v.ExactlyOneOf) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.ExactlyOneOf, topSchemaMap, v) + err := checkKeysAgainstSchemaFlags(k, v.ExactlyOneOf, topSchemaMap, v, true) if err != nil { return fmt.Errorf("ExactlyOneOf: %+v", err) } } if len(v.AtLeastOneOf) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.AtLeastOneOf, topSchemaMap, v) + err := checkKeysAgainstSchemaFlags(k, v.AtLeastOneOf, topSchemaMap, v, true) if err != nil { return fmt.Errorf("AtLeastOneOf: %+v", err) } @@ -897,7 +897,9 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro return nil } -func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap, self *Schema) error { +func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap, self *Schema, requireSelfReference bool) error { + var foundSelfReference bool + for _, key := range keys { parts := strings.Split(key, ".") sm := topSchemaMap @@ -937,7 +939,11 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap } if target == self { - return fmt.Errorf("%s cannot reference self (%s)", k, key) + if !requireSelfReference { + return fmt.Errorf("%s cannot reference self (%s)", k, key) + } + + foundSelfReference = true } if target.Required { @@ -948,6 +954,11 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap return fmt.Errorf("%s cannot contain Computed(When) attribute (%s)", k, key) } } + + 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 1bac8baf03..d5293a6871 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -3315,6 +3315,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { "config_block_attr": { Type: TypeList, Optional: true, + MaxItems: 1, Elem: &Resource{ Schema: map[string]*Schema{ "nested_attr": { @@ -3326,7 +3327,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { }, }, }, - true, // TODO: AtLeastOneOf self reference is necessary + false, }, "AtLeastOneOf list index syntax with list configuration block existing attribute": { @@ -3350,7 +3351,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, }, }, - false, // TODO: AtLeastOneOf self reference is necessary + true, }, "AtLeastOneOf list index syntax with list configuration block missing attribute": { @@ -3558,7 +3559,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"map_attr"}, }, }, - false, // TODO: AtLeastOneOf self reference is necessary + true, }, "AtLeastOneOf string syntax with self reference": { @@ -3569,7 +3570,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"test"}, }, }, - true, // TODO: AtLeastOneOf self reference is necessary + false, }, "ConflictsWith list index syntax with self reference": { @@ -3839,6 +3840,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { "config_block_attr": { Type: TypeList, Optional: true, + MaxItems: 1, Elem: &Resource{ Schema: map[string]*Schema{ "nested_attr": { @@ -3850,7 +3852,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { }, }, }, - true, // TODO: ExactlyOneOf self reference is necessary + false, }, "ExactlyOneOf list index syntax with list configuration block existing attribute": { @@ -3874,7 +3876,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, }, }, - false, // TODO: ExactlyOneOf self reference is necessary + true, }, "ExactlyOneOf list index syntax with list configuration block missing attribute": { @@ -4082,7 +4084,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { ExactlyOneOf: []string{"map_attr"}, }, }, - false, // TODO: ExactlyOneOf self reference is necessary + true, }, "ExactlyOneOf string syntax with self reference": { @@ -4093,7 +4095,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"test"}, }, }, - true, // TODO: ExactlyOneOf self reference is necessary + false, }, "Sub-resource invalid": { From 875d5bd865f123d46354d5705852c33cd1c366d2 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 30 Apr 2020 09:23:55 -0400 Subject: [PATCH 3/3] helper/schema: Adjust checkKeysAgainstSchemaFlags from require to allow, add InternalValidate unit testing for RequiredWhen Reference: https://github.com/hashicorp/terraform-plugin-sdk/pull/418#pullrequestreview-402821058 --- helper/schema/schema.go | 18 +-- helper/schema/schema_test.go | 271 ++++++++++++++++++++++++++++++++++- 2 files changed, 271 insertions(+), 18 deletions(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 93a7a6a101..b1eb8764d7 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -757,7 +757,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) } @@ -897,9 +897,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 @@ -938,12 +936,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 { @@ -955,10 +949,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 d5293a6871..c1b2bf2929 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -3351,7 +3351,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": { @@ -3559,7 +3559,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { AtLeastOneOf: []string{"map_attr"}, }, }, - true, + false, }, "AtLeastOneOf string syntax with self reference": { @@ -3876,7 +3876,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": { @@ -4084,7 +4084,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { ExactlyOneOf: []string{"map_attr"}, }, }, - true, + false, }, "ExactlyOneOf string syntax with self reference": { @@ -4098,6 +4098,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": {