From 9bdc97386f0cfa1e40cdb93226483e26ebc22670 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 24 Sep 2014 23:46:22 -0700 Subject: [PATCH] terraform: expand/contract looks a lot better for modules --- terraform/graph_dot.go | 136 +++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 33 deletions(-) diff --git a/terraform/graph_dot.go b/terraform/graph_dot.go index ffac4a381..c94504d79 100644 --- a/terraform/graph_dot.go +++ b/terraform/graph_dot.go @@ -1,8 +1,8 @@ package terraform import ( - "bytes" "bufio" + "bytes" "fmt" "strings" @@ -29,16 +29,17 @@ func GraphDot(g *depgraph.Graph, opts *GraphDotOpts) string { if opts.depth == 0 { buf.WriteString("digraph {\n") + buf.WriteString("\tcompound = true;\n") } // Determine and add the title // graphDotTitle(buf, g) // Add all the resource. - graphDotAddResources(buf, g) + graphDotAddResources(buf, g, opts) // Add all the resource providers - graphDotAddResourceProviders(buf, g) + graphDotAddResourceProviders(buf, g, opts) // Add all the modules graphDotAddModules(buf, g, opts) @@ -69,13 +70,15 @@ func graphDotAddModules(buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts continue } - if opts.ModuleDepth == opts.depth { - // We're not expanding, so just add the module on its own - graphDotAddModuleSingle(buf, n, opts) - } else { + if graphExpand(opts) { // We're expanding graphDotAddModuleExpand(buf, n, opts) + } else { + // We're not expanding, so just add the module on its own + graphDotAddModuleSingle(buf, n, opts) } + + graphWriteEdges(buf, n, opts) } } @@ -83,11 +86,18 @@ func graphDotAddModuleExpand( buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { m := n.Meta.(*GraphNodeModule) tab := strings.Repeat("\t", opts.depth+1) + uniqueName := graphUniqueName(n, opts) // Wrap ourselves in a subgraph - buf.WriteString(fmt.Sprintf("%ssubgraph \"%s\" {\n", tab, n.Name)) + buf.WriteString(fmt.Sprintf("%ssubgraph \"cluster_%s\" {\n", tab, uniqueName)) defer buf.WriteString(fmt.Sprintf("%s}\n", tab)) + // Add our label so that we have the proper name. + buf.WriteString(fmt.Sprintf("%s\tlabel = \"%s\";\n", tab, n)) + + // Add a hidden name for edges to point from/to + buf.WriteString(fmt.Sprintf("%s\t\"%s_hidden\" [fixedsize=true,width=0,height=0,label=\"\",style=invisible];\n", tab, uniqueName)) + // Graph the subgraph just as we would any other graph subOpts := *opts subOpts.depth++ @@ -103,25 +113,17 @@ func graphDotAddModuleExpand( func graphDotAddModuleSingle( buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { tab := strings.Repeat("\t", opts.depth+1) - - //m := n.Meta.(*GraphNodeModule) + uniqueName := graphUniqueName(n, opts) // Create this node. - buf.WriteString(fmt.Sprintf("%s\"%s\" [\n", tab, n)) + buf.WriteString(fmt.Sprintf("%s\"%s\" [\n", tab, uniqueName)) + buf.WriteString(fmt.Sprintf("%s\tlabel=\"%s\"\n", tab, n)) buf.WriteString(fmt.Sprintf("%s\tshape=component\n", tab)) buf.WriteString(fmt.Sprintf("%s];\n", tab)) - - for _, e := range n.Edges() { - target := e.Tail() - buf.WriteString(fmt.Sprintf( - "%s\"%s\" -> \"%s\";\n", - tab, - n, - target)) - } } -func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { +func graphDotAddResources( + buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) { // Determine if we have diffs. If we do, then we're graphing a // plan, which alters our graph a bit. hasDiff := false @@ -170,8 +172,11 @@ func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { } } + uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) + // Create this node. - buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", n)) + buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", uniqueName)) + buf.WriteString(fmt.Sprintf("\t\t\tlabel=\"%s\"\n", n)) buf.WriteString("\t\t\tshape=box\n") if color != "" { buf.WriteString("\t\t\tstyle=filled\n") @@ -184,10 +189,11 @@ func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { // subgraph. for _, e := range n.Edges() { target := e.Tail() + uniqueTarget := fmt.Sprintf("%d_%s", opts.depth, target) edgeBuf.WriteString(fmt.Sprintf( "\t\"%s\" -> \"%s\";\n", - n, - target)) + uniqueName, + uniqueTarget)) } } buf.WriteString("\t}\n\n") @@ -208,15 +214,18 @@ func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { continue } + uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) + buf.WriteString(fmt.Sprintf( - "\t\t\"%s\" [shape=box,style=filled,color=\"#FF0000\",fillcolor=\"#FF9494\"];\n", n)) + "\t\t\"%s\" [label=\"%s\",shape=box,style=filled,color=\"#FF0000\",fillcolor=\"#FF9494\"];\n", uniqueName, n)) for _, e := range n.Edges() { target := e.Tail() + uniqueTarget := fmt.Sprintf("%d_%s", opts.depth, target) edgeBuf.WriteString(fmt.Sprintf( "\t\"%s\" -> \"%s\";\n", - n, - target)) + uniqueName, + uniqueTarget)) } } buf.WriteString("\t}\n\n") @@ -255,12 +264,14 @@ func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { continue } + uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) for _, e := range edges { target := e.Tail() + uniqueTarget := fmt.Sprintf("%d_%s", opts.depth, target) edgeBuf.WriteString(fmt.Sprintf( "\t\"%s\" -> \"%s\";\n", - n, - target)) + uniqueName, + uniqueTarget)) } } if edgeBuf.Len() > 0 { @@ -269,7 +280,8 @@ func graphDotAddResources(buf *bytes.Buffer, g *depgraph.Graph) { } } -func graphDotAddResourceProviders(buf *bytes.Buffer, g *depgraph.Graph) { +func graphDotAddResourceProviders( + buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) { var edgeBuf bytes.Buffer buf.WriteString("\tsubgraph {\n") for _, n := range g.Nouns { @@ -278,8 +290,11 @@ func graphDotAddResourceProviders(buf *bytes.Buffer, g *depgraph.Graph) { continue } + uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) + // Create this node. - buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", n)) + buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", uniqueName)) + buf.WriteString(fmt.Sprintf("\t\t\tlabel=\"%s\"\n", n)) buf.WriteString("\t\t\tshape=diamond\n") buf.WriteString("\t\t];\n") @@ -287,10 +302,11 @@ func graphDotAddResourceProviders(buf *bytes.Buffer, g *depgraph.Graph) { // subgraph. for _, e := range n.Edges() { target := e.Tail() + uniqueTarget := fmt.Sprintf("%d_%s", target) edgeBuf.WriteString(fmt.Sprintf( "\t\"%s\" -> \"%s\";\n", - n, - target)) + uniqueName, + uniqueTarget)) } } buf.WriteString("\t}\n\n") @@ -324,3 +340,57 @@ func graphDotTitle(buf *bytes.Buffer, g *depgraph.Graph) { buf.WriteString(fmt.Sprintf("\tlabel=\"%s\\n\\n\\n\";\n", title)) buf.WriteString("\tlabelloc=\"t\";\n\n") } + +func graphExpand(opts *GraphDotOpts) bool { + return opts.ModuleDepth > opts.depth || opts.ModuleDepth == -1 +} + +func graphUniqueName(n *depgraph.Noun, opts *GraphDotOpts) string { + return fmt.Sprintf("%d_%s", opts.depth, n) +} + +func graphWriteEdges( + buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { + tab := strings.Repeat("\t", opts.depth+1) + + uniqueName := graphUniqueName(n, opts) + var ltail string + if _, ok := n.Meta.(*GraphNodeModule); ok && graphExpand(opts) { + ltail = "cluster_" + uniqueName + uniqueName = uniqueName + "_hidden" + } + + for _, e := range n.Edges() { + target := e.Tail() + targetN := target.(*depgraph.Noun) + uniqueTarget := graphUniqueName(targetN, opts) + + var lhead string + if _, ok := targetN.Meta.(*GraphNodeModule); ok && graphExpand(opts) { + lhead = "cluster_" + uniqueTarget + uniqueTarget = uniqueTarget + "_hidden" + } + + var attrs string + if lhead != "" || ltail != "" { + var attrList []string + if lhead != "" { + attrList = append(attrList, fmt.Sprintf( + "lhead=\"%s\"", lhead)) + } + if ltail != "" { + attrList = append(attrList, fmt.Sprintf( + "ltail=\"%s\"", ltail)) + } + + attrs = fmt.Sprintf(" [%s]", strings.Join(attrList, ",")) + } + + buf.WriteString(fmt.Sprintf( + "%s\"%s\" -> \"%s\"%s;\n", + tab, + uniqueName, + uniqueTarget, + attrs)) + } +}