terraform: support graphing modules

This commit is contained in:
Mitchell Hashimoto 2014-09-24 17:36:27 -07:00
parent 6904c131a7
commit 72e6f97093
3 changed files with 99 additions and 4 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ terraform.tfstate
bin/ bin/
config/y.go config/y.go
config/y.output config/y.output
modules-dev/
pkg/ pkg/
vendor/ vendor/
website/.vagrant website/.vagrant

View File

@ -16,9 +16,12 @@ type GraphCommand struct {
} }
func (c *GraphCommand) Run(args []string) int { func (c *GraphCommand) Run(args []string) int {
var moduleDepth int
args = c.Meta.process(args, false) args = c.Meta.process(args, false)
cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError) cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError)
cmdFlags.IntVar(&moduleDepth, "module-depth", 0, "module-depth")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return 1 return 1
@ -55,7 +58,11 @@ func (c *GraphCommand) Run(args []string) int {
return 1 return 1
} }
c.Ui.Output(terraform.GraphDot(g)) opts := &terraform.GraphDotOpts{
ModuleDepth: moduleDepth,
}
c.Ui.Output(terraform.GraphDot(g, opts))
return 0 return 0
} }
@ -73,6 +80,11 @@ Usage: terraform graph [options] PATH
read this format is GraphViz, but many web services are also available read this format is GraphViz, but many web services are also available
to read this format. to read this format.
Options:
-module-depth=n The maximum depth to expand modules. By default this is
zero, which will not expand modules at all.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -2,17 +2,34 @@ package terraform
import ( import (
"bytes" "bytes"
"bufio"
"fmt" "fmt"
"strings"
"github.com/hashicorp/terraform/depgraph" "github.com/hashicorp/terraform/depgraph"
"github.com/hashicorp/terraform/digraph" "github.com/hashicorp/terraform/digraph"
) )
// GraphDotOpts are options for turning a graph into dot format.
type GraphDotOpts struct {
// ModuleDepth is the depth of modules to expand. Zero is no expansion,
// one expands the first set of modules, etc. If this is set to -1, then
// all modules are expanded.
ModuleDepth int
// Depth is an internal track of what depth we're at within
// the graph, used to control indentation and other such things.
depth int
}
// GraphDot returns the dot formatting of a visual representation of // GraphDot returns the dot formatting of a visual representation of
// the given Terraform graph. // the given Terraform graph.
func GraphDot(g *depgraph.Graph) string { func GraphDot(g *depgraph.Graph, opts *GraphDotOpts) string {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.WriteString("digraph {\n")
if opts.depth == 0 {
buf.WriteString("digraph {\n")
}
// Determine and add the title // Determine and add the title
// graphDotTitle(buf, g) // graphDotTitle(buf, g)
@ -23,7 +40,13 @@ func GraphDot(g *depgraph.Graph) string {
// Add all the resource providers // Add all the resource providers
graphDotAddResourceProviders(buf, g) graphDotAddResourceProviders(buf, g)
buf.WriteString("}\n") // Add all the modules
graphDotAddModules(buf, g, opts)
if opts.depth == 0 {
buf.WriteString("}\n")
}
return buf.String() return buf.String()
} }
@ -39,6 +62,65 @@ func graphDotAddRoot(buf *bytes.Buffer, n *depgraph.Noun) {
} }
} }
func graphDotAddModules(buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) {
for _, n := range g.Nouns {
_, ok := n.Meta.(*GraphNodeModule)
if !ok {
continue
}
if opts.ModuleDepth == opts.depth {
// We're not expanding, so just add the module on its own
graphDotAddModuleSingle(buf, n, opts)
} else {
// We're expanding
graphDotAddModuleExpand(buf, n, opts)
}
}
}
func graphDotAddModuleExpand(
buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) {
m := n.Meta.(*GraphNodeModule)
tab := strings.Repeat("\t", opts.depth+1)
// Wrap ourselves in a subgraph
buf.WriteString(fmt.Sprintf("%ssubgraph \"%s\" {\n", tab, n.Name))
defer buf.WriteString(fmt.Sprintf("%s}\n", tab))
// Graph the subgraph just as we would any other graph
subOpts := *opts
subOpts.depth++
subStr := GraphDot(m.Graph, &subOpts)
// Tab all the lines of the subgraph
s := bufio.NewScanner(strings.NewReader(subStr))
for s.Scan() {
buf.WriteString(fmt.Sprintf("%s%s\n", tab, s.Text()))
}
}
func graphDotAddModuleSingle(
buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) {
tab := strings.Repeat("\t", opts.depth+1)
//m := n.Meta.(*GraphNodeModule)
// Create this node.
buf.WriteString(fmt.Sprintf("%s\"%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) {
// Determine if we have diffs. If we do, then we're graphing a // Determine if we have diffs. If we do, then we're graphing a
// plan, which alters our graph a bit. // plan, which alters our graph a bit.