From 45c168bb5bfc273cf496ab43cb5d70a2990f8dd6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 3 Jun 2014 16:11:02 -0700 Subject: [PATCH] terraform: multierrors --- terraform/multi_error.go | 50 +++++++++++++++++++++++++++++++ terraform/multi_error_test.go | 56 +++++++++++++++++++++++++++++++++++ terraform/terraform.go | 15 ++++++---- 3 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 terraform/multi_error.go create mode 100644 terraform/multi_error_test.go diff --git a/terraform/multi_error.go b/terraform/multi_error.go new file mode 100644 index 000000000..9f11b95d8 --- /dev/null +++ b/terraform/multi_error.go @@ -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, + } + } +} diff --git a/terraform/multi_error_test.go b/terraform/multi_error_test.go new file mode 100644 index 000000000..44e07babf --- /dev/null +++ b/terraform/multi_error_test.go @@ -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)) + } +} diff --git a/terraform/terraform.go b/terraform/terraform.go index 837e2be01..7290996fb 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -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,