Skip to content

Commit 5f1e6ad

Browse files
committed
terraform: TargetsTransformer should preserve module variables
Fixes hashicorp#10680 This moves TargetsTransformer to run after the transforms that add module variables is run. This makes targeting work across modules (test added). This is a bug that only exists in the new graph, but was caught by a shadow error in hashicorp#10680. Tests were added to protect against regressions.
1 parent a8c6809 commit 5f1e6ad

7 files changed

Lines changed: 115 additions & 6 deletions

File tree

terraform/context_apply_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2708,6 +2708,49 @@ func TestContext2Apply_moduleBool(t *testing.T) {
27082708
}
27092709
}
27102710

2711+
// Tests that a module can be targeted and everything is properly created.
2712+
// This adds to the plan test to also just verify that apply works.
2713+
func TestContext2Apply_moduleTarget(t *testing.T) {
2714+
m := testModule(t, "plan-targeted-cross-module")
2715+
p := testProvider("aws")
2716+
p.ApplyFn = testApplyFn
2717+
p.DiffFn = testDiffFn
2718+
ctx := testContext2(t, &ContextOpts{
2719+
Module: m,
2720+
Providers: map[string]ResourceProviderFactory{
2721+
"aws": testProviderFuncFixed(p),
2722+
},
2723+
Targets: []string{"module.B"},
2724+
})
2725+
2726+
if _, err := ctx.Plan(); err != nil {
2727+
t.Fatalf("err: %s", err)
2728+
}
2729+
2730+
state, err := ctx.Apply()
2731+
if err != nil {
2732+
t.Fatalf("err: %s", err)
2733+
}
2734+
2735+
checkStateString(t, state, `
2736+
<no state>
2737+
module.A:
2738+
aws_instance.foo:
2739+
ID = foo
2740+
foo = bar
2741+
type = aws_instance
2742+
2743+
Outputs:
2744+
2745+
value = foo
2746+
module.B:
2747+
aws_instance.bar:
2748+
ID = foo
2749+
foo = foo
2750+
type = aws_instance
2751+
`)
2752+
}
2753+
27112754
func TestContext2Apply_multiProvider(t *testing.T) {
27122755
m := testModule(t, "apply-multi-provider")
27132756
p := testProvider("aws")

terraform/context_plan_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,47 @@ STATE:
23412341
}
23422342
}
23432343

2344+
// Test that targeting a module properly plans any inputs that depend
2345+
// on another module.
2346+
func TestContext2Plan_targetedCrossModule(t *testing.T) {
2347+
m := testModule(t, "plan-targeted-cross-module")
2348+
p := testProvider("aws")
2349+
p.DiffFn = testDiffFn
2350+
ctx := testContext2(t, &ContextOpts{
2351+
Module: m,
2352+
Providers: map[string]ResourceProviderFactory{
2353+
"aws": testProviderFuncFixed(p),
2354+
},
2355+
Targets: []string{"module.B"},
2356+
})
2357+
2358+
plan, err := ctx.Plan()
2359+
if err != nil {
2360+
t.Fatalf("err: %s", err)
2361+
}
2362+
2363+
actual := strings.TrimSpace(plan.String())
2364+
expected := strings.TrimSpace(`
2365+
DIFF:
2366+
2367+
module.A:
2368+
CREATE: aws_instance.foo
2369+
foo: "" => "bar"
2370+
type: "" => "aws_instance"
2371+
module.B:
2372+
CREATE: aws_instance.bar
2373+
foo: "" => "<computed>"
2374+
type: "" => "aws_instance"
2375+
2376+
STATE:
2377+
2378+
<no state>
2379+
`)
2380+
if actual != expected {
2381+
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
2382+
}
2383+
}
2384+
23442385
func TestContext2Plan_targetedOrphan(t *testing.T) {
23452386
m := testModule(t, "plan-targeted-orphan")
23462387
p := testProvider("aws")

terraform/graph_builder_plan.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
8989
// Attach the state
9090
&AttachStateTransformer{State: b.State},
9191

92+
// Add root variables
93+
&RootVariableTransformer{Module: b.Module},
94+
95+
// Add module variables
96+
&ModuleVariableTransformer{Module: b.Module},
97+
9298
// Connect so that the references are ready for targeting. We'll
9399
// have to connect again later for providers and so on.
94100
&ReferenceTransformer{},
@@ -103,12 +109,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
103109
&ParentProviderTransformer{},
104110
&AttachProviderConfigTransformer{Module: b.Module},
105111

106-
// Add root variables
107-
&RootVariableTransformer{Module: b.Module},
108-
109-
// Add module variables
110-
&ModuleVariableTransformer{Module: b.Module},
111-
112112
// Connect references again to connect the providers, module variables,
113113
// etc. This is idempotent.
114114
&ReferenceTransformer{},

terraform/node_module_variable.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ func (n *NodeApplyableModuleVariable) Path() []string {
3737
return rootModulePath
3838
}
3939

40+
// RemovableIfNotTargeted
41+
func (n *NodeApplyableModuleVariable) RemoveIfNotTargeted() bool {
42+
// We need to add this so that this node will be removed if
43+
// it isn't targeted or a dependency of a target.
44+
return true
45+
}
46+
4047
// GraphNodeReferenceGlobal
4148
func (n *NodeApplyableModuleVariable) ReferenceGlobal() bool {
4249
// We have to create fully qualified references because we cross
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "aws_instance" "foo" {
2+
foo = "bar"
3+
}
4+
5+
output "value" { value = "${aws_instance.foo.id}" }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
variable "input" {}
2+
3+
resource "aws_instance" "bar" {
4+
foo = "${var.input}"
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module "A" {
2+
source = "./A"
3+
}
4+
5+
module "B" {
6+
source = "./B"
7+
input = "${module.A.value}"
8+
}

0 commit comments

Comments
 (0)