terraform: Context introduction
This commit is contained in:
parent
d23733263f
commit
947fa4e669
|
@ -5,6 +5,8 @@ package config
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the configuration that comes from loading a collection
|
// Config is the configuration that comes from loading a collection
|
||||||
|
@ -131,7 +133,7 @@ func (c *Config) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &MultiError{Errors: errs}
|
return &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
package config
|
package multierror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MultiError is an error type to track multiple errors. This is used to
|
// Error is an error type to track multiple errors. This is used to
|
||||||
// accumulate errors in cases such as configuration parsing, and returning
|
// accumulate errors in cases such as configuration parsing, and returning
|
||||||
// them as a single error.
|
// them as a single error.
|
||||||
type MultiError struct {
|
type Error struct {
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *MultiError) Error() string {
|
func (e *Error) Error() string {
|
||||||
points := make([]string, len(e.Errors))
|
points := make([]string, len(e.Errors))
|
||||||
for i, err := range e.Errors {
|
for i, err := range e.Errors {
|
||||||
points[i] = fmt.Sprintf("* %s", err)
|
points[i] = fmt.Sprintf("* %s", err)
|
||||||
|
@ -23,18 +23,18 @@ func (e *MultiError) Error() string {
|
||||||
len(e.Errors), strings.Join(points, "\n"))
|
len(e.Errors), strings.Join(points, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiErrorAppend is a helper function that will append more errors
|
// ErrorAppend is a helper function that will append more errors
|
||||||
// onto a MultiError in order to create a larger multi-error. If the
|
// onto a Error in order to create a larger multi-error. If the
|
||||||
// original error is not a MultiError, it will be turned into one.
|
// original error is not a Error, it will be turned into one.
|
||||||
func MultiErrorAppend(err error, errs ...error) *MultiError {
|
func ErrorAppend(err error, errs ...error) *Error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = new(MultiError)
|
err = new(Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *MultiError:
|
case *Error:
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = new(MultiError)
|
err = new(Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
err.Errors = append(err.Errors, errs...)
|
err.Errors = append(err.Errors, errs...)
|
||||||
|
@ -43,7 +43,7 @@ func MultiErrorAppend(err error, errs ...error) *MultiError {
|
||||||
newErrs := make([]error, len(errs)+1)
|
newErrs := make([]error, len(errs)+1)
|
||||||
newErrs[0] = err
|
newErrs[0] = err
|
||||||
copy(newErrs[1:], errs)
|
copy(newErrs[1:], errs)
|
||||||
return &MultiError{
|
return &Error{
|
||||||
Errors: newErrs,
|
Errors: newErrs,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
package config
|
package multierror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMultiError_Impl(t *testing.T) {
|
func TestError_Impl(t *testing.T) {
|
||||||
var raw interface{}
|
var raw interface{}
|
||||||
raw = &MultiError{}
|
raw = &Error{}
|
||||||
if _, ok := raw.(error); !ok {
|
if _, ok := raw.(error); !ok {
|
||||||
t.Fatal("MultiError must implement error")
|
t.Fatal("Error must implement error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiErrorError(t *testing.T) {
|
func TestErrorError(t *testing.T) {
|
||||||
expected := `2 error(s) occurred:
|
expected := `2 error(s) occurred:
|
||||||
|
|
||||||
* foo
|
* foo
|
||||||
|
@ -24,32 +24,32 @@ func TestMultiErrorError(t *testing.T) {
|
||||||
errors.New("bar"),
|
errors.New("bar"),
|
||||||
}
|
}
|
||||||
|
|
||||||
multi := &MultiError{errors}
|
multi := &Error{errors}
|
||||||
if multi.Error() != expected {
|
if multi.Error() != expected {
|
||||||
t.Fatalf("bad: %s", multi.Error())
|
t.Fatalf("bad: %s", multi.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiErrorAppend_MultiError(t *testing.T) {
|
func TestErrorAppend_Error(t *testing.T) {
|
||||||
original := &MultiError{
|
original := &Error{
|
||||||
Errors: []error{errors.New("foo")},
|
Errors: []error{errors.New("foo")},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := MultiErrorAppend(original, errors.New("bar"))
|
result := ErrorAppend(original, errors.New("bar"))
|
||||||
if len(result.Errors) != 2 {
|
if len(result.Errors) != 2 {
|
||||||
t.Fatalf("wrong len: %d", len(result.Errors))
|
t.Fatalf("wrong len: %d", len(result.Errors))
|
||||||
}
|
}
|
||||||
|
|
||||||
original = &MultiError{}
|
original = &Error{}
|
||||||
result = MultiErrorAppend(original, errors.New("bar"))
|
result = ErrorAppend(original, errors.New("bar"))
|
||||||
if len(result.Errors) != 1 {
|
if len(result.Errors) != 1 {
|
||||||
t.Fatalf("wrong len: %d", len(result.Errors))
|
t.Fatalf("wrong len: %d", len(result.Errors))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiErrorAppend_NonMultiError(t *testing.T) {
|
func TestErrorAppend_NonError(t *testing.T) {
|
||||||
original := errors.New("foo")
|
original := errors.New("foo")
|
||||||
result := MultiErrorAppend(original, errors.New("bar"))
|
result := ErrorAppend(original, errors.New("bar"))
|
||||||
if len(result.Errors) != 2 {
|
if len(result.Errors) != 2 {
|
||||||
t.Fatalf("wrong len: %d", len(result.Errors))
|
t.Fatalf("wrong len: %d", len(result.Errors))
|
||||||
}
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/helper/multierror"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context represents all the context that Terraform needs in order to
|
||||||
|
// perform operations on infrastructure. This structure is built using
|
||||||
|
// ContextOpts and NewContext. See the documentation for those.
|
||||||
|
//
|
||||||
|
// Additionally, a context can be created from a Plan using Plan.Context.
|
||||||
|
type Context struct {
|
||||||
|
config *config.Config
|
||||||
|
diff *Diff
|
||||||
|
hooks []Hook
|
||||||
|
state *State
|
||||||
|
providers map[string]ResourceProviderFactory
|
||||||
|
variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextOpts are the user-creatable configuration structure to create
|
||||||
|
// a context with NewContext.
|
||||||
|
type ContextOpts struct {
|
||||||
|
Config *config.Config
|
||||||
|
Diff *Diff
|
||||||
|
Hooks []Hook
|
||||||
|
State *State
|
||||||
|
Providers map[string]ResourceProviderFactory
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext creates a new context.
|
||||||
|
//
|
||||||
|
// Once a context is created, the pointer values within ContextOpts should
|
||||||
|
// not be mutated in any way, since the pointers are copied, not the values
|
||||||
|
// themselves.
|
||||||
|
func NewContext(opts *ContextOpts) *Context {
|
||||||
|
return &Context{
|
||||||
|
config: opts.Config,
|
||||||
|
diff: opts.Diff,
|
||||||
|
hooks: opts.Hooks,
|
||||||
|
state: opts.State,
|
||||||
|
providers: opts.Providers,
|
||||||
|
variables: opts.Variables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the configuration and returns any warnings or errors.
|
||||||
|
func (c *Context) Validate() ([]string, []error) {
|
||||||
|
var rerr *multierror.Error
|
||||||
|
|
||||||
|
// Validate the configuration itself
|
||||||
|
if err := c.config.Validate(); err != nil {
|
||||||
|
rerr = multierror.ErrorAppend(rerr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the user variables
|
||||||
|
if errs := smcUserVariables(c.config, c.variables); len(errs) > 0 {
|
||||||
|
rerr = multierror.ErrorAppend(rerr, errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
if rerr != nil && len(rerr.Errors) > 0 {
|
||||||
|
errs = rerr.Errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContextValidate(t *testing.T) {
|
||||||
|
config := testConfig(t, "validate-good")
|
||||||
|
c := testContext(t, &ContextOpts{
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
|
||||||
|
w, e := c.Validate()
|
||||||
|
if len(w) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", w)
|
||||||
|
}
|
||||||
|
if len(e) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextValidate_badVar(t *testing.T) {
|
||||||
|
config := testConfig(t, "validate-bad-var")
|
||||||
|
c := testContext(t, &ContextOpts{
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
|
||||||
|
w, e := c.Validate()
|
||||||
|
if len(w) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", w)
|
||||||
|
}
|
||||||
|
if len(e) == 0 {
|
||||||
|
t.Fatalf("bad: %#v", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextValidate_requiredVar(t *testing.T) {
|
||||||
|
config := testConfig(t, "validate-required-var")
|
||||||
|
c := testContext(t, &ContextOpts{
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
|
||||||
|
w, e := c.Validate()
|
||||||
|
if len(w) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", w)
|
||||||
|
}
|
||||||
|
if len(e) == 0 {
|
||||||
|
t.Fatalf("bad: %#v", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testContext(t *testing.T, opts *ContextOpts) *Context {
|
||||||
|
return NewContext(opts)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
"github.com/hashicorp/terraform/depgraph"
|
"github.com/hashicorp/terraform/depgraph"
|
||||||
|
"github.com/hashicorp/terraform/helper/multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GraphOpts are options used to create the resource graph that Terraform
|
// GraphOpts are options used to create the resource graph that Terraform
|
||||||
|
@ -325,7 +326,7 @@ func graphAddMissingResourceProviders(
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &MultiError{Errors: errs}
|
return &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -518,7 +519,7 @@ func graphInitResourceProviders(
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &MultiError{Errors: errs}
|
return &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -583,7 +584,7 @@ func graphMapResourceProviders(g *depgraph.Graph) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &MultiError{Errors: errs}
|
return &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
"github.com/hashicorp/terraform/depgraph"
|
"github.com/hashicorp/terraform/depgraph"
|
||||||
|
"github.com/hashicorp/terraform/helper/multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Terraform is the primary structure that is used to interact with
|
// Terraform is the primary structure that is used to interact with
|
||||||
|
@ -282,7 +283,7 @@ func (t *Terraform) applyWalkFn(
|
||||||
// Determine the new state and update variables
|
// Determine the new state and update variables
|
||||||
err = nil
|
err = nil
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
err = &MultiError{Errors: errs}
|
err = &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.Vars(), err
|
return r.Vars(), err
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
num = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "bar" {
|
||||||
|
foo = "${var.foo}"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
num = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "bar" {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
variable "foo" {}
|
||||||
|
|
||||||
|
resource "aws_instance" "web" {
|
||||||
|
ami = "${var.foo}"
|
||||||
|
}
|
Loading…
Reference in New Issue