helper/schema: validate ConflictsWith against top-level

The runtime impl of ConfictsWith uses Resource.Get(), which makes it
work with any other attribute of the resource - the InternalValidate was
only checking against the local schemaMap though, preventing subResource
from using ConflictsWith properly.

It's a lot of wiring and it's a bit ugly, but it's not runtime code, so
I'm a bit less concerned about that aspect.

This should take care of the problem mentioned in #1909
This commit is contained in:
Paul Hinze 2015-05-12 09:45:15 -05:00
parent 931d05198c
commit 1e3d1b07e6
3 changed files with 36 additions and 8 deletions

View File

@ -61,12 +61,13 @@ func (p *Provider) InternalValidate() error {
return errors.New("provider is nil")
}
if err := schemaMap(p.Schema).InternalValidate(); err != nil {
sm := schemaMap(p.Schema)
if err := sm.InternalValidate(sm); err != nil {
return err
}
for k, r := range p.ResourcesMap {
if err := r.InternalValidate(); err != nil {
if err := r.InternalValidate(nil); err != nil {
return fmt.Errorf("%s: %s", k, err)
}
}

View File

@ -220,10 +220,11 @@ func (r *Resource) Refresh(
// Provider.InternalValidate() will automatically call this for all of
// the resources it manages, so you don't need to call this manually if it
// is part of a Provider.
func (r *Resource) InternalValidate() error {
func (r *Resource) InternalValidate(topSchemaMap schemaMap) error {
if r == nil {
return errors.New("resource is nil")
}
tsm := topSchemaMap
if r.isTopLevel() {
// All non-Computed attributes must be ForceNew if Update is not defined
@ -239,9 +240,10 @@ func (r *Resource) InternalValidate() error {
"No Update defined, must set ForceNew on: %#v", nonForceNewAttrs)
}
}
tsm = schemaMap(r.Schema)
}
return schemaMap(r.Schema).InternalValidate()
return schemaMap(r.Schema).InternalValidate(tsm)
}
// Returns true if the resource is "top level" i.e. not a sub-resource.

View File

@ -17,6 +17,7 @@ import (
"reflect"
"sort"
"strconv"
"strings"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/mapstructure"
@ -410,7 +411,10 @@ func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) {
// InternalValidate validates the format of this schema. This should be called
// from a unit test (and not in user-path code) to verify that a schema
// is properly built.
func (m schemaMap) InternalValidate() error {
func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error {
if topSchemaMap == nil {
topSchemaMap = m
}
for k, v := range m {
if v.Type == TypeInvalid {
return fmt.Errorf("%s: Type must be specified", k)
@ -446,11 +450,32 @@ func (m schemaMap) InternalValidate() error {
if len(v.ConflictsWith) > 0 {
for _, key := range v.ConflictsWith {
if m[key].Required {
parts := strings.Split(key, ".")
sm := topSchemaMap
var target *Schema
for _, part := range parts {
// Skip index fields
if _, err := strconv.Atoi(part); err == nil {
continue
}
var ok bool
if target, ok = sm[part]; !ok {
return fmt.Errorf("%s: ConflictsWith references unknown attribute (%s)", k, key)
}
if subResource, ok := target.Elem.(*Resource); ok {
sm = schemaMap(subResource.Schema)
}
}
if target == nil {
return fmt.Errorf("%s: ConflictsWith cannot find target attribute (%s), sm: %#v", k, key, sm)
}
if target.Required {
return fmt.Errorf("%s: ConflictsWith cannot contain Required attribute (%s)", k, key)
}
if m[key].Computed || len(m[key].ComputedWhen) > 0 {
if target.Computed || len(target.ComputedWhen) > 0 {
return fmt.Errorf("%s: ConflictsWith cannot contain Computed(When) attribute (%s)", k, key)
}
}
@ -473,7 +498,7 @@ func (m schemaMap) InternalValidate() error {
switch t := v.Elem.(type) {
case *Resource:
if err := t.InternalValidate(); err != nil {
if err := t.InternalValidate(topSchemaMap); err != nil {
return err
}
case *Schema: