diff --git a/helper/schema/schema.go b/helper/schema/schema.go index de05df66f..77c50de2b 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1266,6 +1266,13 @@ func (m schemaMap) validate( "%q: this field cannot be set", k)} } + if raw == config.UnknownVariableValue { + // If the value is unknown then we can't validate it yet. + // In particular, this avoids spurious type errors where downstream + // validation code sees UnknownVariableValue as being just a string. + return nil, nil + } + err := m.validateConflictingAttributes(k, schema, c) if err != nil { return nil, []error{err} @@ -1283,10 +1290,15 @@ func (m schemaMap) validateConflictingAttributes( return nil } - for _, conflicting_key := range schema.ConflictsWith { - if _, ok := c.Get(conflicting_key); ok { + for _, conflictingKey := range schema.ConflictsWith { + if raw, ok := c.Get(conflictingKey); ok { + if raw == config.UnknownVariableValue { + // An unknown value might become unset (null) once known, so + // we must defer validation until it's known. + continue + } return fmt.Errorf( - "%q: conflicts with %s", k, conflicting_key) + "%q: conflicts with %s", k, conflictingKey) } } diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index a82f8b2d5..7e28d39d9 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -4356,10 +4356,11 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, }, - "Not a list": { + "Not a list nested block": { Schema: map[string]*Schema{ "ingress": &Schema{ - Type: TypeList, + Type: TypeList, + Optional: true, Elem: &Resource{ Schema: map[string]*Schema{ "from": &Schema{ @@ -4376,6 +4377,48 @@ func TestSchemaMap_Validate(t *testing.T) { }, Err: true, + Errors: []error{ + fmt.Errorf(`ingress: should be a list`), + }, + }, + + "Not a list primitive": { + Schema: map[string]*Schema{ + "strings": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": "foo", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`strings: should be a list`), + }, + }, + + "Unknown list": { + Schema: map[string]*Schema{ + "strings": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": config.UnknownVariableValue, + }, + + Err: false, }, "Required sub-resource field": { @@ -4870,6 +4913,80 @@ func TestSchemaMap_Validate(t *testing.T) { }, }, + "Conflicting attributes okay when unknown 1": { + Schema: map[string]*Schema{ + "whitelist": &Schema{ + Type: TypeString, + Optional: true, + }, + "blacklist": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": "white-val", + "blacklist": config.UnknownVariableValue, + }, + + Err: false, + }, + + "Conflicting attributes okay when unknown 2": { + Schema: map[string]*Schema{ + "whitelist": &Schema{ + Type: TypeString, + Optional: true, + }, + "blacklist": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": config.UnknownVariableValue, + "blacklist": "black-val", + }, + + Err: false, + }, + + "Conflicting attributes generate error even if one is unknown": { + Schema: map[string]*Schema{ + "whitelist": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"blacklist", "greenlist"}, + }, + "blacklist": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist", "greenlist"}, + }, + "greenlist": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": config.UnknownVariableValue, + "blacklist": "black-val", + "greenlist": "green-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("\"blacklist\": conflicts with greenlist"), + fmt.Errorf("\"greenlist\": conflicts with blacklist"), + }, + }, + "Required attribute & undefined conflicting optional are good": { Schema: map[string]*Schema{ "required_att": &Schema{