diff --git a/terraform/graph.go b/terraform/graph.go index af99bcf2d3..50988ff330 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -102,6 +102,35 @@ func GraphFull(g *depgraph.Graph, ps map[string]ResourceProviderFactory) error { return nil } +// GraphAddDiff takes an already-built graph of resources and adds the +// diffs to the resource nodes themselves. +// +// This may also introduces new graph elements. If there are diffs that +// require a destroy, new elements may be introduced since destroy order +// is different than create order. For example, destroying a VPC requires +// 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 { + for _, n := range g.Nouns { + rn, ok := n.Meta.(*GraphNodeResource) + if !ok { + continue + } + + rd, ok := d.Resources[rn.Resource.Id] + if !ok { + continue + } + + rn.Resource.Diff = rd + } + + return nil +} + // configGraph turns a configuration structure into a dependency graph. func graphAddConfigResources( g *depgraph.Graph, c *config.Config, s *State) { diff --git a/terraform/graph_test.go b/terraform/graph_test.go index 76a795e2ae..7b2634754d 100644 --- a/terraform/graph_test.go +++ b/terraform/graph_test.go @@ -1,6 +1,7 @@ package terraform import ( + "reflect" "strings" "testing" ) @@ -109,6 +110,50 @@ func TestGraphFull(t *testing.T) { } } +func TestGraphAddDiff(t *testing.T) { + config := testConfig(t, "graph-diff") + + g := Graph(config, nil) + if err := g.Validate(); err != nil { + t.Fatalf("err: %s", err) + } + + diff := &Diff{ + Resources: map[string]*ResourceDiff{ + "aws_instance.foo": &ResourceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": &ResourceAttrDiff{ + New: "bar", + }, + }, + }, + }, + } + + 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(testTerraformGraphDiffStr) + 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 @@ -130,6 +175,13 @@ root root -> provider.aws ` +const testTerraformGraphDiffStr = ` +root: root +aws_instance.foo +root + root -> aws_instance.foo +` + const testTerraformGraphStateStr = ` root: root aws_instance.old diff --git a/terraform/terraform.go b/terraform/terraform.go index 384b9e7d17..f16155c610 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -104,10 +104,6 @@ func (t *Terraform) apply( p *Plan) (*State, error) { s := new(State) err := g.Walk(t.applyWalkFn(s, p)) - - // Now that we've built the state and have the graph, re-calculate - // the dependencies for our state based on what we did. - return s, err } diff --git a/terraform/test-fixtures/graph-diff/main.tf b/terraform/test-fixtures/graph-diff/main.tf new file mode 100644 index 0000000000..ca956330c7 --- /dev/null +++ b/terraform/test-fixtures/graph-diff/main.tf @@ -0,0 +1,2 @@ +resource "aws_instance" "foo" { +}