Skip to content

Commit 46b7828

Browse files
authored
Merge pull request hashicorp#7865 from hashicorp/b-filter-untargeted-variables
terraform: Filter untargeted variable nodes
2 parents 9034196 + 24c45fc commit 46b7828

5 files changed

Lines changed: 82 additions & 4 deletions

File tree

terraform/context_plan_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,43 @@ module.child:
21142114
}
21152115
}
21162116

2117+
func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) {
2118+
m := testModule(t, "plan-targeted-module-untargeted-variable")
2119+
p := testProvider("aws")
2120+
p.DiffFn = testDiffFn
2121+
ctx := testContext2(t, &ContextOpts{
2122+
Module: m,
2123+
Providers: map[string]ResourceProviderFactory{
2124+
"aws": testProviderFuncFixed(p),
2125+
},
2126+
Targets: []string{"aws_instance.blue", "module.blue_mod"},
2127+
})
2128+
2129+
plan, err := ctx.Plan()
2130+
if err != nil {
2131+
t.Fatalf("err: %s", err)
2132+
}
2133+
2134+
actual := strings.TrimSpace(plan.String())
2135+
expected := strings.TrimSpace(`
2136+
DIFF:
2137+
2138+
CREATE: aws_instance.blue
2139+
2140+
module.blue_mod:
2141+
CREATE: aws_instance.mod
2142+
type: "" => "aws_instance"
2143+
value: "" => "<computed>"
2144+
2145+
STATE:
2146+
2147+
<no state>
2148+
`)
2149+
if actual != expected {
2150+
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
2151+
}
2152+
}
2153+
21172154
// https://github.com/hashicorp/terraform/issues/4515
21182155
func TestContext2Plan_targetedOverTen(t *testing.T) {
21192156
m := testModule(t, "plan-targeted-over-ten")

terraform/graph_config_node_variable.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ func (n *GraphNodeConfigVariable) DependableName() []string {
3636
return []string{n.Name()}
3737
}
3838

39+
// RemoveIfNotTargeted implements RemovableIfNotTargeted.
40+
// When targeting is active, variables that are not targeted should be removed
41+
// from the graph, because otherwise module variables trying to interpolate
42+
// their references can fail when they're missing the referent resource node.
43+
func (n *GraphNodeConfigVariable) RemoveIfNotTargeted() bool {
44+
return true
45+
}
46+
3947
func (n *GraphNodeConfigVariable) DependentOn() []string {
4048
// If we don't have any value set, we don't depend on anything
4149
if n.Value == nil {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
variable "id" {}
2+
3+
resource "aws_instance" "mod" {
4+
value = "${var.id}"
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
resource "aws_instance" "blue" { }
2+
resource "aws_instance" "green" { }
3+
4+
module "blue_mod" {
5+
source = "./child"
6+
id = "${aws_instance.blue.id}"
7+
}
8+
9+
module "green_mod" {
10+
source = "./child"
11+
id = "${aws_instance.green.id}"
12+
}

terraform/transform_targets.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ func (t *TargetsTransformer) Transform(g *Graph) error {
3737
}
3838

3939
for _, v := range g.Vertices() {
40+
removable := false
4041
if _, ok := v.(GraphNodeAddressable); ok {
41-
if !targetedNodes.Include(v) {
42-
log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
43-
g.Remove(v)
44-
}
42+
removable = true
43+
}
44+
if vr, ok := v.(RemovableIfNotTargeted); ok {
45+
removable = vr.RemoveIfNotTargeted()
46+
}
47+
if removable && !targetedNodes.Include(v) {
48+
log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
49+
g.Remove(v)
4550
}
4651
}
4752
}
@@ -110,3 +115,14 @@ func (t *TargetsTransformer) nodeIsTarget(
110115
}
111116
return false
112117
}
118+
119+
// RemovableIfNotTargeted is a special interface for graph nodes that
120+
// aren't directly addressable, but need to be removed from the graph when they
121+
// are not targeted. (Nodes that are not directly targeted end up in the set of
122+
// targeted nodes because something that _is_ targeted depends on them.) The
123+
// initial use case for this interface is GraphNodeConfigVariable, which was
124+
// having trouble interpolating for module variables in targeted scenarios that
125+
// filtered out the resource node being referenced.
126+
type RemovableIfNotTargeted interface {
127+
RemoveIfNotTargeted() bool
128+
}

0 commit comments

Comments
 (0)