Merge pull request #12383 from hashicorp/jbardin/multi-var-errs

report all errors from module validation
This commit is contained in:
James Bardin 2017-03-14 15:36:32 -04:00 committed by GitHub
commit 2e3579b058
3 changed files with 67 additions and 32 deletions

View File

@ -1 +1,2 @@
variable "memory" {}
variable "feature" {}

View File

@ -259,7 +259,7 @@ func (t *Tree) Validate() error {
}
// If something goes wrong, here is our error template
newErr := &TreeError{Name: []string{t.Name()}}
newErr := &treeError{Name: []string{t.Name()}}
// Terraform core does not handle root module children named "root".
// We plan to fix this in the future but this bug was brought up in
@ -271,15 +271,14 @@ func (t *Tree) Validate() error {
// Validate our configuration first.
if err := t.config.Validate(); err != nil {
newErr.Err = err
return newErr
newErr.Add(err)
}
// If we're the root, we do extra validation. This validation usually
// requires the entire tree (since children don't have parent pointers).
if len(t.path) == 0 {
if err := t.validateProviderAlias(); err != nil {
return err
newErr.Add(err)
}
}
@ -293,7 +292,7 @@ func (t *Tree) Validate() error {
continue
}
verr, ok := err.(*TreeError)
verr, ok := err.(*treeError)
if !ok {
// Unknown error, just return...
return err
@ -301,7 +300,7 @@ func (t *Tree) Validate() error {
// Append ourselves to the error and then return
verr.Name = append(verr.Name, t.Name())
return verr
newErr.AddChild(verr)
}
// Go over all the modules and verify that any parameters are valid
@ -327,10 +326,9 @@ func (t *Tree) Validate() error {
// Compare to the keys in our raw config for the module
for k, _ := range m.RawConfig.Raw {
if _, ok := varMap[k]; !ok {
newErr.Err = fmt.Errorf(
newErr.Add(fmt.Errorf(
"module %s: %s is not a valid parameter",
m.Name, k)
return newErr
m.Name, k))
}
// Remove the required
@ -339,10 +337,9 @@ func (t *Tree) Validate() error {
// If we have any required left over, they aren't set.
for k, _ := range requiredMap {
newErr.Err = fmt.Errorf(
"module %s: required variable %s not set",
m.Name, k)
return newErr
newErr.Add(fmt.Errorf(
"module %s: required variable %q not set",
m.Name, k))
}
}
@ -369,33 +366,61 @@ func (t *Tree) Validate() error {
}
}
if !found {
newErr.Err = fmt.Errorf(
newErr.Add(fmt.Errorf(
"%s: %s is not a valid output for module %s",
source, mv.Field, mv.Name)
return newErr
source, mv.Field, mv.Name))
}
}
}
return newErr.ErrOrNil()
}
// treeError is an error use by Tree.Validate to accumulates all
// validation errors.
type treeError struct {
Name []string
Errs []error
Children []*treeError
}
func (e *treeError) Add(err error) {
e.Errs = append(e.Errs, err)
}
func (e *treeError) AddChild(err *treeError) {
e.Children = append(e.Children, err)
}
func (e *treeError) ErrOrNil() error {
if len(e.Errs) > 0 || len(e.Children) > 0 {
return e
}
return nil
}
// TreeError is an error returned by Tree.Validate if an error occurs
// with validation.
type TreeError struct {
Name []string
Err error
}
func (e *treeError) Error() string {
name := strings.Join(e.Name, ".")
var out bytes.Buffer
fmt.Fprintf(&out, "module %s: ", name)
func (e *TreeError) Error() string {
// Build up the name
var buf bytes.Buffer
for _, n := range e.Name {
buf.WriteString(n)
buf.WriteString(".")
if len(e.Errs) == 1 {
// single like error
out.WriteString(e.Errs[0].Error())
} else {
// multi-line error
for _, err := range e.Errs {
fmt.Fprintf(&out, "\n %s", err)
}
}
buf.Truncate(buf.Len() - 1)
// Format the value
return fmt.Sprintf("module %s: %s", buf.String(), e.Err)
if len(e.Children) > 0 {
// start the next error on a new line
out.WriteString("\n ")
}
for _, child := range e.Children {
out.WriteString(child.Error())
}
return out.String()
}

View File

@ -410,9 +410,18 @@ func TestTreeValidate_requiredChildVar(t *testing.T) {
t.Fatalf("err: %s", err)
}
if err := tree.Validate(); err == nil {
err := tree.Validate()
if err == nil {
t.Fatal("should error")
}
// ensure both variables are mentioned in the output
errMsg := err.Error()
for _, v := range []string{"feature", "memory"} {
if !strings.Contains(errMsg, v) {
t.Fatalf("no mention of missing variable %q", v)
}
}
}
const treeLoadStr = `