terraform: graph can add "destroy" nodes

This commit is contained in:
Mitchell Hashimoto 2014-06-30 19:10:44 -07:00
parent 6f274eb7a9
commit 2d72164c6a
3 changed files with 147 additions and 2 deletions

View File

@ -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
}

View File

@ -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

View File

@ -0,0 +1,2 @@
resource "aws_instance" "foo" {
}