From deec7194a3ea08b27dad45e8e0c6ba9c6d32e608 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 19 Oct 2014 20:33:00 -0700 Subject: [PATCH] helper/schema: validate maps properly [GH-461] --- helper/schema/schema.go | 40 ++++++++++++++++++++++ helper/schema/schema_test.go | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index c0fb99210..d19add38a 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -767,6 +767,44 @@ func (m schemaMap) validateList( return ws, es } +func (m schemaMap) validateMap( + k string, + raw interface{}, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + // 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) + switch rawV.Kind() { + case reflect.Map: + case reflect.Slice: + default: + return nil, []error{fmt.Errorf( + "%s: should be a map", k)} + } + + // If it is not a slice, it is valid + if rawV.Kind() != reflect.Slice { + return nil, nil + } + + // It is a slice, verify that all the elements are maps + raws := make([]interface{}, rawV.Len()) + for i, _ := range raws { + raws[i] = rawV.Index(i).Interface() + } + + for _, raw := range raws { + v := reflect.ValueOf(raw) + if v.Kind() != reflect.Map { + return nil, []error{fmt.Errorf( + "%s: should be a map", k)} + } + } + + return nil, nil +} + func (m schemaMap) validateObject( k string, schema map[string]*Schema, @@ -823,6 +861,8 @@ func (m schemaMap) validatePrimitive( fallthrough case TypeList: return m.validateList(k, raw, schema, c) + case TypeMap: + return m.validateMap(k, raw, schema, c) case TypeBool: // Verify that we can parse this as the correct type var n bool diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index e966eb34b..3fc303e85 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -2009,6 +2009,71 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, }, + + // Maps + { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": "foo", + }, + + Err: true, + }, + + { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + + { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + "foo", + }, + }, + + Err: true, + }, } for i, tc := range cases {