Merge pull request #997 from hashicorp/b-validate-subresource

helper/schema: validate subresources more effectively
This commit is contained in:
Mitchell Hashimoto 2015-02-18 10:23:12 -08:00
commit a91eaa1fce
3 changed files with 87 additions and 27 deletions

View File

@ -1007,19 +1007,13 @@ func (m schemaMap) validateObject(
} }
// Detect any extra/unknown keys and report those as errors. // Detect any extra/unknown keys and report those as errors.
prefix := k + "." raw, _ := c.Get(k)
for configK, _ := range c.Raw { if m, ok := raw.(map[string]interface{}); ok {
if k != "" { for subk, _ := range m {
if !strings.HasPrefix(configK, prefix) { if _, ok := schema[subk]; !ok {
continue es = append(es, fmt.Errorf(
"%s: invalid or unknown key: %s", k, subk))
} }
configK = configK[len(prefix):]
}
if _, ok := schema[configK]; !ok {
es = append(es, fmt.Errorf(
"%s: invalid or unknown key: %s", k, configK))
} }
} }

View File

@ -2484,7 +2484,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Warn bool Warn bool
Err bool Err bool
}{ }{
// Good // #0 Good
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2500,7 +2500,7 @@ func TestSchemaMap_Validate(t *testing.T) {
}, },
}, },
// Good, because the var is not set and that error will come elsewhere // #1 Good, because the var is not set and that error will come elsewhere
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"size": &Schema{ "size": &Schema{
@ -2518,7 +2518,7 @@ func TestSchemaMap_Validate(t *testing.T) {
}, },
}, },
// Required field not set // #2 Required field not set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2532,7 +2532,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Invalid type // #3 Invalid type
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"port": &Schema{ "port": &Schema{
@ -2548,6 +2548,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// #4
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"user_data": &Schema{ "user_data": &Schema{
@ -2567,7 +2568,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Bad type, interpolated // #5 Bad type, interpolated
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"size": &Schema{ "size": &Schema{
@ -2587,7 +2588,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Required but has DefaultFunc // #6 Required but has DefaultFunc
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2602,7 +2603,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Config: nil, Config: nil,
}, },
// Required but has DefaultFunc return nil // #7 Required but has DefaultFunc return nil
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2619,7 +2620,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Optional sub-resource // #8 Optional sub-resource
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -2640,7 +2641,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: false, Err: false,
}, },
// Not a list // #9 Not a list
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -2663,7 +2664,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Required sub-resource field // #10 Required sub-resource field
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -2688,7 +2689,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Good sub-resource // #11 Good sub-resource
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -2716,7 +2717,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: false, Err: false,
}, },
// Invalid/unknown field // #12 Invalid/unknown field
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2734,7 +2735,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Computed field set // #13 Computed field set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -2750,7 +2751,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Not a set // #14 Not a set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -2770,7 +2771,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// Maps // #15 Maps
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"user_data": &Schema{ "user_data": &Schema{
@ -2786,6 +2787,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// #16
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"user_data": &Schema{ "user_data": &Schema{
@ -2803,6 +2805,7 @@ func TestSchemaMap_Validate(t *testing.T) {
}, },
}, },
// #17
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"user_data": &Schema{ "user_data": &Schema{
@ -2818,6 +2821,7 @@ func TestSchemaMap_Validate(t *testing.T) {
}, },
}, },
// #18
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"user_data": &Schema{ "user_data": &Schema{
@ -2835,6 +2839,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// #19
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"security_groups": &Schema{ "security_groups": &Schema{
@ -2856,6 +2861,7 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: false, Err: false,
}, },
// #20
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"security_groups": &Schema{ "security_groups": &Schema{
@ -2873,6 +2879,63 @@ func TestSchemaMap_Validate(t *testing.T) {
Err: true, Err: true,
}, },
// #21 Bad, subresource should not allow unknown elements
{
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"port": &Schema{
Type: TypeInt,
Required: true,
},
},
},
},
},
Config: map[string]interface{}{
"ingress": []interface{}{
map[string]interface{}{
"port": 80,
"other": "yes",
},
},
},
Err: true,
},
// #22 Bad, subresource should not allow invalid types
{
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"port": &Schema{
Type: TypeInt,
Required: true,
},
},
},
},
},
Config: map[string]interface{}{
"ingress": []interface{}{
map[string]interface{}{
"port": "bad",
},
},
},
Err: true,
},
} }
for i, tc := range cases { for i, tc := range cases {

View File

@ -162,6 +162,9 @@ func (c *ResourceConfig) IsSet(k string) bool {
func (c *ResourceConfig) get( func (c *ResourceConfig) get(
k string, raw map[string]interface{}) (interface{}, bool) { k string, raw map[string]interface{}) (interface{}, bool) {
parts := strings.Split(k, ".") parts := strings.Split(k, ".")
if len(parts) == 1 && parts[0] == "" {
parts = nil
}
var current interface{} = raw var current interface{} = raw
for _, part := range parts { for _, part := range parts {