Skip to content

Commit b0eafeb

Browse files
committed
core: fix deadlock w/ CBD + modules
fixes hashicorp#1947 Root cause was a bad edge being made by the CBD transform going from the flattened destroy node to the unflattened create node, which was no longer in the graph. The destroy node therefore had a dependency that could never be satisfied, which locked up the walk.
1 parent 725f667 commit b0eafeb

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎terraform/context_test.go‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,56 @@ func TestContext2Plan_moduleCycle(t *testing.T) {
139139
}
140140
}
141141

142+
func TestContext2Plan_moduleDeadlock(t *testing.T) {
143+
m := testModule(t, "plan-module-deadlock")
144+
p := testProvider("aws")
145+
p.DiffFn = testDiffFn
146+
timeout := make(chan bool, 1)
147+
done := make(chan bool, 1)
148+
go func() {
149+
time.Sleep(3 * time.Second)
150+
timeout <- true
151+
}()
152+
go func() {
153+
ctx := testContext2(t, &ContextOpts{
154+
Module: m,
155+
Providers: map[string]ResourceProviderFactory{
156+
"aws": testProviderFuncFixed(p),
157+
},
158+
})
159+
160+
plan, err := ctx.Plan()
161+
done <- true
162+
if err != nil {
163+
t.Fatalf("err: %s", err)
164+
}
165+
166+
actual := strings.TrimSpace(plan.String())
167+
expected := strings.TrimSpace(`
168+
DIFF:
169+
170+
module.child:
171+
CREATE: aws_instance.foo.0
172+
CREATE: aws_instance.foo.1
173+
CREATE: aws_instance.foo.2
174+
175+
STATE:
176+
177+
<no state>
178+
`)
179+
if actual != expected {
180+
t.Fatalf("expected:\n%sgot:\n%s", expected, actual)
181+
}
182+
}()
183+
184+
select {
185+
case <-timeout:
186+
t.Fatalf("timed out! probably deadlock")
187+
case <-done:
188+
// ok
189+
}
190+
}
191+
142192
func TestContext2Plan_moduleInput(t *testing.T) {
143193
m := testModule(t, "plan-module-input")
144194
p := testProvider("aws")

‎terraform/graph_config_node_resource.go‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,17 @@ func (n *GraphNodeConfigResourceFlat) DestroyNode(mode GraphNodeDestroyMode) Gra
311311
return &graphNodeResourceDestroyFlat{
312312
graphNodeResourceDestroy: node,
313313
PathValue: n.PathValue,
314+
FlatCreateNode: n,
314315
}
315316
}
316317

317318
type graphNodeResourceDestroyFlat struct {
318319
*graphNodeResourceDestroy
319320

320321
PathValue []string
322+
323+
// Needs to be able to properly yield back a flattened create node to prevent
324+
FlatCreateNode *GraphNodeConfigResourceFlat
321325
}
322326

323327
func (n *graphNodeResourceDestroyFlat) Name() string {
@@ -329,6 +333,10 @@ func (n *graphNodeResourceDestroyFlat) Path() []string {
329333
return n.PathValue
330334
}
331335

336+
func (n *graphNodeResourceDestroyFlat) CreateNode() dag.Vertex {
337+
return n.FlatCreateNode
338+
}
339+
332340
// graphNodeResourceDestroy represents the logical destruction of a
333341
// resource. This node doesn't mean it will be destroyed for sure, but
334342
// instead that if a destroy were to happen, it must happen at this point.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "aws_instance" "foo" {
2+
count = "${length("abc")}"
3+
lifecycle { create_before_destroy = true }
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module "child" {
2+
source = "./child"
3+
}

0 commit comments

Comments
 (0)