From 8c9097f4543e22cdc8e3b6f1cd8a415282094940 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 11 Oct 2016 15:51:27 +0800 Subject: [PATCH] terraform: orphaned grandchild module inherits provider config This fixes an issue where orphaned grandchild modules don't properly inherit their provider configurations from grandparents. I found this while working on shadow graphs (the shadow graph actually caught an inconsistency between runs and exposed this bug!), so I'm unsure if this affects any issue. To better explain the issue, I'll diagram things. Here is a hierarchy that _works_ (w/o this PR): ``` root |-- child1 (orphan) |-- child2 |-- grandchild ``` All modules in this case will successfully inherit provider configurations from "root". Here is a hierarchy that _doesn't work without this PR_: ``` root |-- child1 (orphan) |-- grandchild (orphan) ``` In this case, `child1` does successfully inherit the provider from root, but `grandchild` _will not_ unless `child1` had resources. If `child1` has no resources, it wouldn't inherit anything. This PR fixes that. --- terraform/context_apply_test.go | 48 +++++++++++++++++++++++++++++++++ terraform/transform_provider.go | 7 ++--- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 74fcf33bd..0296ef487 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -1390,6 +1390,54 @@ func TestContext2Apply_moduleOrphanProvider(t *testing.T) { } } +func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { + m := testModule(t, "apply-module-orphan-provider-inherit") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + p.ConfigureFn = func(c *ResourceConfig) error { + if _, ok := c.Get("value"); !ok { + return fmt.Errorf("value is not found") + } + + return nil + } + + // Create a state with an orphan module that is nested (grandchild) + state := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root", "parent", "child"}, + Resources: map[string]*ResourceState{ + "aws_instance.bar": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + }, + }, + }, + }, + } + + ctx := testContext2(t, &ContextOpts{ + Module: m, + State: state, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + if _, err := ctx.Apply(); err != nil { + t.Fatalf("err: %s", err) + } +} + func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { m := testModule(t, "apply-module-grandchild-provider-inherit") p := testProvider("aws") diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index cbb56dec1..99ea0c3f4 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -523,9 +523,10 @@ func (n *graphNodeProviderFlat) DependableName() []string { func (n *graphNodeProviderFlat) DependentOn() []string { var result []string - // If we're in a module, then depend on our parent's provider - if len(n.PathValue) > 1 { - prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) + // If we're in a module, then depend on all parent providers. Some of + // these may not exist, hence we depend on all of them. + for i := len(n.PathValue); i > 1; i-- { + prefix := modulePrefixStr(n.PathValue[:i-1]) result = modulePrefixList(n.graphNodeProvider.DependableName(), prefix) }