mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-27 09:21:14 -06:00
terraform: graph can add "destroy" nodes
This commit is contained in:
parent
6f274eb7a9
commit
2d72164c6a
@ -111,9 +111,8 @@ func GraphFull(g *depgraph.Graph, ps map[string]ResourceProviderFactory) error {
|
||||
// destroying the VPC's subnets first, whereas creating a VPC requires
|
||||
// doing it before the subnets are created. This function handles inserting
|
||||
// these nodes for you.
|
||||
//
|
||||
// Note that all nodes modifying the same resource will have the same name.
|
||||
func GraphAddDiff(g *depgraph.Graph, d *Diff) error {
|
||||
var nlist []*depgraph.Noun
|
||||
for _, n := range g.Nouns {
|
||||
rn, ok := n.Meta.(*GraphNodeResource)
|
||||
if !ok {
|
||||
@ -124,10 +123,79 @@ func GraphAddDiff(g *depgraph.Graph, d *Diff) error {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if rd.Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
if rd.Destroy || rd.RequiresNew() {
|
||||
// If we're destroying, we create a new destroy node with
|
||||
// the proper dependencies. Perform a dirty copy operation.
|
||||
newNode := new(GraphNodeResource)
|
||||
*newNode = *rn
|
||||
newNode.Resource = new(Resource)
|
||||
*newNode.Resource = *rn.Resource
|
||||
|
||||
// Make the diff _just_ the destroy.
|
||||
newNode.Resource.Diff = &ResourceDiff{Destroy: true}
|
||||
|
||||
// Append it to the list so we handle it later
|
||||
deps := make([]*depgraph.Dependency, len(n.Deps))
|
||||
copy(deps, n.Deps)
|
||||
newN := &depgraph.Noun{
|
||||
Name: fmt.Sprintf("%s (destroy)", newNode.Resource.Id),
|
||||
Meta: newNode,
|
||||
Deps: deps,
|
||||
}
|
||||
nlist = append(nlist, newN)
|
||||
|
||||
// Mark the old diff to not destroy since we handle that in
|
||||
// the dedicated node.
|
||||
rd.Destroy = false
|
||||
|
||||
// Add to the new noun to our dependencies so that the destroy
|
||||
// happens before the apply.
|
||||
n.Deps = append(n.Deps, &depgraph.Dependency{
|
||||
Name: newN.Name,
|
||||
Source: n,
|
||||
Target: newN,
|
||||
})
|
||||
}
|
||||
|
||||
rn.Resource.Diff = rd
|
||||
}
|
||||
|
||||
// Go through each noun and make sure we calculate all the dependencies
|
||||
// properly.
|
||||
for _, n := range nlist {
|
||||
rn := n.Meta.(*GraphNodeResource)
|
||||
|
||||
// If we have no dependencies, then just continue
|
||||
deps := rn.Resource.State.Dependencies
|
||||
if len(deps) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have dependencies. We must be destroyed BEFORE those
|
||||
// dependencies. Look to see if they're managed.
|
||||
for _, dep := range deps {
|
||||
for _, n2 := range nlist {
|
||||
rn2 := n2.Meta.(*GraphNodeResource)
|
||||
if rn2.Resource.State.ID == dep.ID {
|
||||
n2.Deps = append(n2.Deps, &depgraph.Dependency{
|
||||
Name: n.Name,
|
||||
Source: n2,
|
||||
Target: n,
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the nouns to the graph
|
||||
g.Nouns = append(g.Nouns, nlist...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -154,6 +154,67 @@ func TestGraphAddDiff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphAddDiff_destroy(t *testing.T) {
|
||||
config := testConfig(t, "graph-diff-destroy")
|
||||
state := &State{
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": &ResourceState{
|
||||
ID: "foo",
|
||||
Type: "aws_instance",
|
||||
},
|
||||
|
||||
"aws_instance.bar": &ResourceState{
|
||||
ID: "bar",
|
||||
Type: "aws_instance",
|
||||
Dependencies: []ResourceDependency{
|
||||
ResourceDependency{
|
||||
ID: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
g := Graph(config, state)
|
||||
if err := g.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
diff := &Diff{
|
||||
Resources: map[string]*ResourceDiff{
|
||||
"aws_instance.foo": &ResourceDiff{
|
||||
Destroy: true,
|
||||
},
|
||||
"aws_instance.bar": &ResourceDiff{
|
||||
Destroy: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := GraphAddDiff(g, diff); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if err := g.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(testTerraformGraphDiffDestroyStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
|
||||
// Verify that the state has been added
|
||||
n := g.Noun("aws_instance.foo")
|
||||
rn := n.Meta.(*GraphNodeResource)
|
||||
|
||||
expected2 := diff.Resources["aws_instance.foo"]
|
||||
actual2 := rn.Resource.Diff
|
||||
if !reflect.DeepEqual(actual2, expected2) {
|
||||
t.Fatalf("bad: %#v", actual2)
|
||||
}
|
||||
}
|
||||
|
||||
const testTerraformGraphStr = `
|
||||
root: root
|
||||
aws_instance.web
|
||||
@ -182,6 +243,20 @@ root
|
||||
root -> aws_instance.foo
|
||||
`
|
||||
|
||||
const testTerraformGraphDiffDestroyStr = `
|
||||
root: root
|
||||
aws_instance.bar
|
||||
aws_instance.bar -> aws_instance.bar (destroy)
|
||||
aws_instance.bar (destroy)
|
||||
aws_instance.foo
|
||||
aws_instance.foo -> aws_instance.foo (destroy)
|
||||
aws_instance.foo (destroy)
|
||||
aws_instance.foo (destroy) -> aws_instance.bar (destroy)
|
||||
root
|
||||
root -> aws_instance.bar
|
||||
root -> aws_instance.foo
|
||||
`
|
||||
|
||||
const testTerraformGraphStateStr = `
|
||||
root: root
|
||||
aws_instance.old
|
||||
|
2
terraform/test-fixtures/graph-diff-destroy/main.tf
Normal file
2
terraform/test-fixtures/graph-diff-destroy/main.tf
Normal file
@ -0,0 +1,2 @@
|
||||
resource "aws_instance" "foo" {
|
||||
}
|
Loading…
Reference in New Issue
Block a user