Merge pull request #12383 from hashicorp/jbardin/multi-var-errs
report all errors from module validation
This commit is contained in:
commit
2e3579b058
|
@ -1 +1,2 @@
|
||||||
variable "memory" {}
|
variable "memory" {}
|
||||||
|
variable "feature" {}
|
||||||
|
|
|
@ -259,7 +259,7 @@ func (t *Tree) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If something goes wrong, here is our error template
|
// 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".
|
// 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
|
// 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.
|
// Validate our configuration first.
|
||||||
if err := t.config.Validate(); err != nil {
|
if err := t.config.Validate(); err != nil {
|
||||||
newErr.Err = err
|
newErr.Add(err)
|
||||||
return newErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're the root, we do extra validation. This validation usually
|
// If we're the root, we do extra validation. This validation usually
|
||||||
// requires the entire tree (since children don't have parent pointers).
|
// requires the entire tree (since children don't have parent pointers).
|
||||||
if len(t.path) == 0 {
|
if len(t.path) == 0 {
|
||||||
if err := t.validateProviderAlias(); err != nil {
|
if err := t.validateProviderAlias(); err != nil {
|
||||||
return err
|
newErr.Add(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +292,7 @@ func (t *Tree) Validate() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
verr, ok := err.(*TreeError)
|
verr, ok := err.(*treeError)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Unknown error, just return...
|
// Unknown error, just return...
|
||||||
return err
|
return err
|
||||||
|
@ -301,7 +300,7 @@ func (t *Tree) Validate() error {
|
||||||
|
|
||||||
// Append ourselves to the error and then return
|
// Append ourselves to the error and then return
|
||||||
verr.Name = append(verr.Name, t.Name())
|
verr.Name = append(verr.Name, t.Name())
|
||||||
return verr
|
newErr.AddChild(verr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go over all the modules and verify that any parameters are valid
|
// 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
|
// Compare to the keys in our raw config for the module
|
||||||
for k, _ := range m.RawConfig.Raw {
|
for k, _ := range m.RawConfig.Raw {
|
||||||
if _, ok := varMap[k]; !ok {
|
if _, ok := varMap[k]; !ok {
|
||||||
newErr.Err = fmt.Errorf(
|
newErr.Add(fmt.Errorf(
|
||||||
"module %s: %s is not a valid parameter",
|
"module %s: %s is not a valid parameter",
|
||||||
m.Name, k)
|
m.Name, k))
|
||||||
return newErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the required
|
// Remove the required
|
||||||
|
@ -339,10 +337,9 @@ func (t *Tree) Validate() error {
|
||||||
|
|
||||||
// If we have any required left over, they aren't set.
|
// If we have any required left over, they aren't set.
|
||||||
for k, _ := range requiredMap {
|
for k, _ := range requiredMap {
|
||||||
newErr.Err = fmt.Errorf(
|
newErr.Add(fmt.Errorf(
|
||||||
"module %s: required variable %s not set",
|
"module %s: required variable %q not set",
|
||||||
m.Name, k)
|
m.Name, k))
|
||||||
return newErr
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,33 +366,61 @@ func (t *Tree) Validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
newErr.Err = fmt.Errorf(
|
newErr.Add(fmt.Errorf(
|
||||||
"%s: %s is not a valid output for module %s",
|
"%s: %s is not a valid output for module %s",
|
||||||
source, mv.Field, mv.Name)
|
source, mv.Field, mv.Name))
|
||||||
return newErr
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeError is an error returned by Tree.Validate if an error occurs
|
func (e *treeError) Error() string {
|
||||||
// with validation.
|
name := strings.Join(e.Name, ".")
|
||||||
type TreeError struct {
|
var out bytes.Buffer
|
||||||
Name []string
|
fmt.Fprintf(&out, "module %s: ", name)
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *TreeError) Error() string {
|
if len(e.Errs) == 1 {
|
||||||
// Build up the name
|
// single like error
|
||||||
var buf bytes.Buffer
|
out.WriteString(e.Errs[0].Error())
|
||||||
for _, n := range e.Name {
|
} else {
|
||||||
buf.WriteString(n)
|
// multi-line error
|
||||||
buf.WriteString(".")
|
for _, err := range e.Errs {
|
||||||
|
fmt.Fprintf(&out, "\n %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf.Truncate(buf.Len() - 1)
|
|
||||||
|
|
||||||
// Format the value
|
if len(e.Children) > 0 {
|
||||||
return fmt.Sprintf("module %s: %s", buf.String(), e.Err)
|
// start the next error on a new line
|
||||||
|
out.WriteString("\n ")
|
||||||
|
}
|
||||||
|
for _, child := range e.Children {
|
||||||
|
out.WriteString(child.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,9 +410,18 @@ func TestTreeValidate_requiredChildVar(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tree.Validate(); err == nil {
|
err := tree.Validate()
|
||||||
|
if err == nil {
|
||||||
t.Fatal("should error")
|
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 = `
|
const treeLoadStr = `
|
||||||
|
|
Loading…
Reference in New Issue