Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helper/schema: validate ConflictsWith against top-level #1926

Merged
merged 2 commits into from
May 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions helper/schema/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
6 changes: 4 additions & 2 deletions helper/schema/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion helper/schema/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func TestResourceInternalValidate(t *testing.T) {
}

for i, tc := range cases {
err := tc.In.InternalValidate()
err := tc.In.InternalValidate(schemaMap{})
if (err != nil) != tc.Err {
t.Fatalf("%d: bad: %s", i, err)
}
Expand Down
33 changes: 29 additions & 4 deletions helper/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"reflect"
"sort"
"strconv"
"strings"

"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion helper/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2799,7 +2799,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) {
}

for i, tc := range cases {
err := schemaMap(tc.In).InternalValidate()
err := schemaMap(tc.In).InternalValidate(schemaMap{})
if (err != nil) != tc.Err {
if tc.Err {
t.Fatalf("%d: Expected error did not occur:\n\n%#v", i, tc.In)
Expand Down