terraform: multierrors

This commit is contained in:
Mitchell Hashimoto 2014-06-03 16:11:02 -07:00
parent 45a8deb388
commit 45c168bb5b
3 changed files with 115 additions and 6 deletions

50
terraform/multi_error.go Normal file
View File

@ -0,0 +1,50 @@
package terraform
import (
"fmt"
"strings"
)
// MultiError is an error type to track multiple errors. This is used to
// accumulate errors in cases such as configuration parsing, and returning
// them as a single error.
type MultiError struct {
Errors []error
}
func (e *MultiError) Error() string {
points := make([]string, len(e.Errors))
for i, err := range e.Errors {
points[i] = fmt.Sprintf("* %s", err)
}
return fmt.Sprintf(
"%d error(s) occurred:\n\n%s",
len(e.Errors), strings.Join(points, "\n"))
}
// MultiErrorAppend is a helper function that will append more errors
// onto a MultiError in order to create a larger multi-error. If the
// original error is not a MultiError, it will be turned into one.
func MultiErrorAppend(err error, errs ...error) *MultiError {
if err == nil {
err = new(MultiError)
}
switch err := err.(type) {
case *MultiError:
if err == nil {
err = new(MultiError)
}
err.Errors = append(err.Errors, errs...)
return err
default:
newErrs := make([]error, len(errs)+1)
newErrs[0] = err
copy(newErrs[1:], errs)
return &MultiError{
Errors: newErrs,
}
}
}

View File

@ -0,0 +1,56 @@
package terraform
import (
"errors"
"testing"
)
func TestMultiError_Impl(t *testing.T) {
var raw interface{}
raw = &MultiError{}
if _, ok := raw.(error); !ok {
t.Fatal("MultiError must implement error")
}
}
func TestMultiErrorError(t *testing.T) {
expected := `2 error(s) occurred:
* foo
* bar`
errors := []error{
errors.New("foo"),
errors.New("bar"),
}
multi := &MultiError{errors}
if multi.Error() != expected {
t.Fatalf("bad: %s", multi.Error())
}
}
func TestMultiErrorAppend_MultiError(t *testing.T) {
original := &MultiError{
Errors: []error{errors.New("foo")},
}
result := MultiErrorAppend(original, errors.New("bar"))
if len(result.Errors) != 2 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
original = &MultiError{}
result = MultiErrorAppend(original, errors.New("bar"))
if len(result.Errors) != 1 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}
func TestMultiErrorAppend_NonMultiError(t *testing.T) {
original := errors.New("foo")
result := MultiErrorAppend(original, errors.New("bar"))
if len(result.Errors) != 2 {
t.Fatalf("wrong len: %d", len(result.Errors))
}
}

View File

@ -31,6 +31,8 @@ type Config struct {
// time, as well as richer checks such as verifying that the resource providers
// can be properly initialized, can be configured, etc.
func New(c *Config) (*Terraform, error) {
var errs []error
// Validate that all required variables have values
required := make(map[string]struct{})
for k, v := range c.Config.Variables {
@ -42,14 +44,10 @@ func New(c *Config) (*Terraform, error) {
delete(required, k)
}
if len(required) > 0 {
errs := make([]error, 0, len(required))
for k, _ := range required {
errs = append(errs, fmt.Errorf(
"Required variable not set: %s", k))
}
// TODO(mitchellh): Return multi-error
return nil, errs[0]
}
// Go through each resource and match it up to a provider
@ -90,14 +88,19 @@ func New(c *Config) (*Terraform, error) {
if provider == nil {
// We never found a matching provider.
return nil, fmt.Errorf(
errs = append(errs, fmt.Errorf(
"Provider for resource %s not found.",
r.Id())
r.Id()))
}
mapping[r] = provider
}
// If we accumulated any errors, then return them all
if len(errs) > 0 {
return nil, &MultiError{Errors: errs}
}
return &Terraform{
config: c.Config,
mapping: mapping,