diff --git a/terraform/testdata/transform-destroy-cbd-edge-multi/main.tf b/terraform/testdata/transform-destroy-cbd-edge-multi/main.tf new file mode 100644 index 000000000..964bc44cf --- /dev/null +++ b/terraform/testdata/transform-destroy-cbd-edge-multi/main.tf @@ -0,0 +1,15 @@ +resource "test_object" "A" { + lifecycle { + create_before_destroy = true + } +} + +resource "test_object" "B" { + lifecycle { + create_before_destroy = true + } +} + +resource "test_object" "C" { + test_string = "${test_object.A.id}-${test_object.B.id}" +} diff --git a/terraform/transform_destroy_cbd.go b/terraform/transform_destroy_cbd.go index 5945eeadd..410a709ea 100644 --- a/terraform/transform_destroy_cbd.go +++ b/terraform/transform_destroy_cbd.go @@ -259,7 +259,9 @@ func (t *CBDEdgeTransformer) depMap(g *Graph, destroyMap map[string][]dag.Vertex // Build the list of destroy nodes that each resource address should depend // on. For example, when we find B, we map the address of B to A_d in the // "depMap" variable below. - depMap := make(map[string][]dag.Vertex) + + // Use a nested map to remove duplicate edges. + depMap := make(map[string]map[dag.Vertex]struct{}) for _, v := range g.Vertices() { // We're looking for resources. rn, ok := v.(GraphNodeResource) @@ -294,12 +296,24 @@ func (t *CBDEdgeTransformer) depMap(g *Graph, destroyMap map[string][]dag.Vertex // needs to depend on. key := rn.ResourceAddr().String() - // we only need to record the same dns once - if _, ok := depMap[key]; !ok { - depMap[key] = append(depMap[key], dns...) + deps, ok := depMap[key] + if !ok { + deps = make(map[dag.Vertex]struct{}) } + + for _, d := range dns { + deps[d] = struct{}{} + } + depMap[key] = deps } } - return depMap, nil + result := map[string][]dag.Vertex{} + for k, m := range depMap { + for v := range m { + result[k] = append(result[k], v) + } + } + + return result, nil } diff --git a/terraform/transform_destroy_cbd_test.go b/terraform/transform_destroy_cbd_test.go index 9d320d776..13deaa336 100644 --- a/terraform/transform_destroy_cbd_test.go +++ b/terraform/transform_destroy_cbd_test.go @@ -95,6 +95,53 @@ test_object.B } } +func TestCBDEdgeTransformerMulti(t *testing.T) { + changes := &plans.Changes{ + Resources: []*plans.ResourceInstanceChangeSrc{ + { + Addr: mustResourceInstanceAddr("test_object.A"), + ChangeSrc: plans.ChangeSrc{ + Action: plans.CreateThenDelete, + }, + }, + { + Addr: mustResourceInstanceAddr("test_object.B"), + ChangeSrc: plans.ChangeSrc{ + Action: plans.CreateThenDelete, + }, + }, + { + Addr: mustResourceInstanceAddr("test_object.C"), + ChangeSrc: plans.ChangeSrc{ + Action: plans.Update, + }, + }, + }, + } + + g := cbdTestGraph(t, "transform-destroy-cbd-edge-multi", changes) + g = filterInstances(g) + + actual := strings.TrimSpace(g.String()) + expected := regexp.MustCompile(strings.TrimSpace(` +(?m)test_object.A +test_object.A \(destroy deposed \w+\) + test_object.A + test_object.C +test_object.B +test_object.B \(destroy deposed \w+\) + test_object.B + test_object.C +test_object.C + test_object.A + test_object.B +`)) + + if !expected.MatchString(actual) { + t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) + } +} + func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) { changes := &plans.Changes{ Resources: []*plans.ResourceInstanceChangeSrc{