diff --git a/terraform/graph.go b/terraform/graph.go index d257c1c8e..e2318c5c3 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -234,6 +234,9 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) { // Add the provider dependencies graphAddResourceProviderDeps(g) + // Now, prune the providers that we aren't using. + graphPruneResourceProviders(g) + // Add explicit dependsOn dependencies to the graph graphAddExplicitDeps(g) @@ -1177,6 +1180,47 @@ func graphAddResourceProviderDeps(g *depgraph.Graph) { } } +// graphPruneResourceProviders will remove the GraphNodeResourceProvider +// nodes that aren't used in any way. +func graphPruneResourceProviders(g *depgraph.Graph) { + // First, build a mapping of the providers we have. + ps := make(map[string]struct{}) + for _, n := range g.Nouns { + _, ok := n.Meta.(*GraphNodeResourceProvider) + if !ok { + continue + } + + ps[n.Name] = struct{}{} + } + + // Now go through all the dependencies throughout and find + // if any of these aren't reachable. + for _, n := range g.Nouns { + for _, dep := range n.Deps { + delete(ps, dep.Target.Name) + } + } + + if len(ps) == 0 { + // We used all of them! + return + } + + // Now go through and remove these nodes that aren't used + for i := 0; i < len(g.Nouns); i++ { + if _, ok := ps[g.Nouns[i].Name]; !ok { + continue + } + + // Delete this node + copy(g.Nouns[i:], g.Nouns[i+1:]) + g.Nouns[len(g.Nouns)-1] = nil + g.Nouns = g.Nouns[:len(g.Nouns)-1] + i-- + } +} + // graphMapResourceProviderId goes through the graph and maps the // ID of a resource provider node to each resource. This lets us know which // configuration is for which resource. diff --git a/terraform/graph_test.go b/terraform/graph_test.go index f965fdc8f..689e6d259 100644 --- a/terraform/graph_test.go +++ b/terraform/graph_test.go @@ -159,6 +159,21 @@ func TestGraph_moduleOrphan(t *testing.T) { } } +func TestGraph_providerPrune(t *testing.T) { + m := testModule(t, "graph-provider-prune") + + g, err := Graph(&GraphOpts{Module: m}) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(testTerraformGraphProviderPruneStr) + if actual != expected { + t.Fatalf("bad:\n\n%s", actual) + } +} + func TestGraph_state(t *testing.T) { m := testModule(t, "graph-basic") state := &State{ @@ -910,6 +925,15 @@ root root -> aws_instance.old ` +const testTerraformGraphProviderPruneStr = ` +root: root +aws_load_balancer.weblb + aws_load_balancer.weblb -> provider.aws +provider.aws +root + root -> aws_load_balancer.weblb +` + const testTerraformGraphStateStr = ` root: root aws_instance.old diff --git a/terraform/test-fixtures/graph-provider-prune/main.tf b/terraform/test-fixtures/graph-provider-prune/main.tf new file mode 100644 index 000000000..ac2f526fa --- /dev/null +++ b/terraform/test-fixtures/graph-provider-prune/main.tf @@ -0,0 +1,5 @@ +provider "aws" {} +provider "digitalocean" {} +provider "openstack" {} + +resource "aws_load_balancer" "weblb" {}