don't reflect nil in schema validation

Nil values were not previously expected during validation, but they can
appear in some situations with the new protocol. Add checks to prevent
using zero reflect.Values.
This commit is contained in:
James Bardin 2019-07-27 12:11:11 -07:00
parent 112b7755c0
commit 016c4f782d
2 changed files with 56 additions and 0 deletions

View File

@ -1440,6 +1440,11 @@ func (m schemaMap) validateList(
}
}
// schemaMap can't validate nil
if raw == nil {
return nil, nil
}
// We use reflection to verify the slice because you can't
// case to []interface{} unless the slice is exactly that type.
rawV := reflect.ValueOf(raw)
@ -1518,6 +1523,10 @@ func (m schemaMap) validateMap(
}
}
// schemaMap can't validate nil
if raw == nil {
return nil, nil
}
// We use reflection to verify the slice because you can't
// case to []interface{} unless the slice is exactly that type.
rawV := reflect.ValueOf(raw)
@ -1644,6 +1653,12 @@ func (m schemaMap) validateObject(
schema map[string]*Schema,
c *terraform.ResourceConfig) ([]string, []error) {
raw, _ := c.Get(k)
// schemaMap can't validate nil
if raw == nil {
return nil, nil
}
if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) {
return nil, []error{fmt.Errorf(
"%s: expected object, got %s",
@ -1688,6 +1703,14 @@ func (m schemaMap) validatePrimitive(
raw interface{},
schema *Schema,
c *terraform.ResourceConfig) ([]string, []error) {
// a nil value shouldn't happen in the old protocol, and in the new
// protocol the types have already been validated. Either way, we can't
// reflect on nil, so don't panic.
if raw == nil {
return nil, nil
}
// Catch if the user gave a complex type where a primitive was
// expected, so we can return a friendly error message that
// doesn't contain Go type system terminology.

View File

@ -5465,6 +5465,39 @@ func TestSchemaMap_Validate(t *testing.T) {
},
Err: false,
},
"unexpected nils values": {
Schema: map[string]*Schema{
"strings": &Schema{
Type: TypeList,
Optional: true,
Elem: &Schema{
Type: TypeString,
},
},
"block": &Schema{
Type: TypeList,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"int": &Schema{
Type: TypeInt,
Required: true,
},
},
},
},
},
Config: map[string]interface{}{
"strings": []interface{}{"1", nil},
"block": []interface{}{map[string]interface{}{
"int": nil,
},
nil,
},
},
},
}
for tn, tc := range cases {