diff --git a/terraform/context.go b/terraform/context.go index e1d9cf91a..511d30c0a 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "log" "sort" "sync" @@ -388,19 +389,7 @@ func (c *Context) walk(operation walkOperation) (*ContextGraphWalker, error) { } // Walk the graph + log.Printf("[INFO] Starting graph walk: %s", operation.String()) walker := &ContextGraphWalker{Context: c, Operation: operation} return walker, graph.Walk(walker) } - -// walkOperation is an enum which tells the walkContext what to do. -type walkOperation byte - -const ( - walkInvalid walkOperation = iota - walkInput - walkApply - walkPlan - walkPlanDestroy - walkRefresh - walkValidate -) diff --git a/terraform/context_test.go b/terraform/context_test.go index 4d64982fa..0fb90ed1a 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -3175,6 +3175,45 @@ func TestContext2Apply_module(t *testing.T) { } } +func TestContext2Apply_multiProvider(t *testing.T) { + m := testModule(t, "apply-multi-provider") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + pDO := testProvider("do") + pDO.ApplyFn = testApplyFn + pDO.DiffFn = testDiffFn + + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + "do": testProviderFuncFixed(pDO), + }, + }) + + if _, err := ctx.Plan(nil); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + mod := state.RootModule() + if len(mod.Resources) < 2 { + t.Fatalf("bad: %#v", mod.Resources) + } + + actual := strings.TrimSpace(state.String()) + expected := strings.TrimSpace(testTerraformApplyMultiProviderStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } +} + func TestContext2Apply_nilDiff(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") diff --git a/terraform/eval.go b/terraform/eval.go index 7b1afeb38..73a323a67 100644 --- a/terraform/eval.go +++ b/terraform/eval.go @@ -2,6 +2,7 @@ package terraform import ( "log" + "strings" ) // EvalNode is the interface that must be implemented by graph nodes to @@ -43,10 +44,15 @@ func Eval(n EvalNode, ctx EvalContext) (interface{}, error) { // EvalRaw is like Eval except that it returns all errors, even if they // signal something normal such as EvalEarlyExitError. func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) { - log.Printf("[DEBUG] eval: %T", n) + path := "unknown" + if ctx != nil { + path = strings.Join(ctx.Path(), ".") + } + + log.Printf("[DEBUG] %s: eval: %T", path, n) output, err := n.Eval(ctx) if err != nil { - log.Printf("[ERROR] eval: %T, err: %s", n, err) + log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) } return output, err diff --git a/terraform/eval_context_builtin.go b/terraform/eval_context_builtin.go index 6908cd620..f5c96bd30 100644 --- a/terraform/eval_context_builtin.go +++ b/terraform/eval_context_builtin.go @@ -78,7 +78,11 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) return nil, err } - ctx.ProviderCache[PathCacheKey(ctx.Path())] = p + providerPath := make([]string, len(ctx.Path())+1) + copy(providerPath, ctx.Path()) + providerPath[len(providerPath)-1] = n + + ctx.ProviderCache[PathCacheKey(providerPath)] = p return p, nil } @@ -88,7 +92,11 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider { ctx.ProviderLock.Lock() defer ctx.ProviderLock.Unlock() - return ctx.ProviderCache[PathCacheKey(ctx.Path())] + providerPath := make([]string, len(ctx.Path())+1) + copy(providerPath, ctx.Path()) + providerPath[len(providerPath)-1] = n + + return ctx.ProviderCache[PathCacheKey(providerPath)] } func (ctx *BuiltinEvalContext) ConfigureProvider( diff --git a/terraform/graph_walk_operation.go b/terraform/graph_walk_operation.go new file mode 100644 index 000000000..c2143fbd8 --- /dev/null +++ b/terraform/graph_walk_operation.go @@ -0,0 +1,16 @@ +package terraform + +//go:generate stringer -type=walkOperation graph_walk_operation.go + +// walkOperation is an enum which tells the walkContext what to do. +type walkOperation byte + +const ( + walkInvalid walkOperation = iota + walkInput + walkApply + walkPlan + walkPlanDestroy + walkRefresh + walkValidate +) diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index d6d8a305b..2581b760d 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -278,6 +278,17 @@ module.child: type = aws_instance ` +const testTerraformApplyMultiProviderStr = ` +aws_instance.bar: + ID = foo + foo = bar + type = aws_instance +do_instance.foo: + ID = foo + num = 2 + type = do_instance +` + const testTerraformApplyProvisionerStr = ` aws_instance.bar: ID = foo diff --git a/terraform/test-fixtures/apply-multi-provider/main.tf b/terraform/test-fixtures/apply-multi-provider/main.tf new file mode 100644 index 000000000..4ee94a3bf --- /dev/null +++ b/terraform/test-fixtures/apply-multi-provider/main.tf @@ -0,0 +1,7 @@ +resource "do_instance" "foo" { + num = "2" +} + +resource "aws_instance" "bar" { + foo = "bar" +} diff --git a/terraform/walkoperation_string.go b/terraform/walkoperation_string.go new file mode 100644 index 000000000..ddd894f53 --- /dev/null +++ b/terraform/walkoperation_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=walkOperation graph_walk_operation.go; DO NOT EDIT + +package terraform + +import "fmt" + +const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidate" + +var _walkOperation_index = [...]uint8{0, 11, 20, 29, 37, 52, 63, 75} + +func (i walkOperation) String() string { + if i+1 >= walkOperation(len(_walkOperation_index)) { + return fmt.Sprintf("walkOperation(%d)", i) + } + return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]] +}