terraform: goodbye graph.go
This commit is contained in:
parent
b1e5b32322
commit
af1778cd5e
|
@ -1,376 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/depgraph"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// the given Terraform graph.
|
|
||||||
func GraphDot(g *depgraph.Graph, opts *GraphDotOpts) string {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
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, opts)
|
|
||||||
|
|
||||||
// Add all the resource providers
|
|
||||||
graphDotAddResourceProviders(buf, g, opts)
|
|
||||||
|
|
||||||
// Add all the modules
|
|
||||||
graphDotAddModules(buf, g, opts)
|
|
||||||
|
|
||||||
if opts.depth == 0 {
|
|
||||||
buf.WriteString("}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func graphDotAddRoot(buf *bytes.Buffer, n *depgraph.Noun) {
|
|
||||||
buf.WriteString(fmt.Sprintf("\t\"%s\" [shape=circle];\n", "root"))
|
|
||||||
|
|
||||||
for _, e := range n.Edges() {
|
|
||||||
target := e.Tail()
|
|
||||||
buf.WriteString(fmt.Sprintf(
|
|
||||||
"\t\"%s\" -> \"%s\";\n",
|
|
||||||
"root",
|
|
||||||
target))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func graphDotAddModules(buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) {
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
_, ok := n.Meta.(*GraphNodeModule)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 \"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++
|
|
||||||
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)
|
|
||||||
uniqueName := graphUniqueName(n, opts)
|
|
||||||
|
|
||||||
// Create this node.
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
rn, ok := n.Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() {
|
|
||||||
hasDiff = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var edgeBuf bytes.Buffer
|
|
||||||
// Do all the non-destroy resources
|
|
||||||
buf.WriteString("\tsubgraph {\n")
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
rn, ok := n.Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rn.Resource.Diff != nil && rn.Resource.Diff.Destroy {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have diffs then we're graphing a plan. If we don't have
|
|
||||||
// have a diff on this resource, don't graph anything, since the
|
|
||||||
// plan wouldn't do anything to this resource.
|
|
||||||
if hasDiff {
|
|
||||||
if rn.Resource.Diff == nil || rn.Resource.Diff.Empty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the colors. White = no change, yellow = change,
|
|
||||||
// green = create. Destroy is in the next section.
|
|
||||||
var color, fillColor string
|
|
||||||
if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() {
|
|
||||||
if rn.Resource.State != nil && rn.Resource.State.ID != "" {
|
|
||||||
color = "#FFFF00"
|
|
||||||
fillColor = "#FFFF94"
|
|
||||||
} else {
|
|
||||||
color = "#00FF00"
|
|
||||||
fillColor = "#9EFF9E"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueName := fmt.Sprintf("%d_%s", opts.depth, n)
|
|
||||||
|
|
||||||
// Create this node.
|
|
||||||
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")
|
|
||||||
buf.WriteString(fmt.Sprintf("\t\t\tcolor=\"%s\"\n", color))
|
|
||||||
buf.WriteString(fmt.Sprintf("\t\t\tfillcolor=\"%s\"\n", fillColor))
|
|
||||||
}
|
|
||||||
buf.WriteString("\t\t];\n")
|
|
||||||
|
|
||||||
// Build up all the edges in a separate buffer so they're not in the
|
|
||||||
// subgraph.
|
|
||||||
graphWriteEdges(&edgeBuf, n, opts)
|
|
||||||
}
|
|
||||||
buf.WriteString("\t}\n\n")
|
|
||||||
if edgeBuf.Len() > 0 {
|
|
||||||
buf.WriteString(edgeBuf.String())
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do all the destroy resources
|
|
||||||
edgeBuf.Reset()
|
|
||||||
buf.WriteString("\tsubgraph {\n")
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
rn, ok := n.Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rn.Resource.Diff == nil || !rn.Resource.Diff.Destroy {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueName := fmt.Sprintf("%d_%s", opts.depth, n)
|
|
||||||
|
|
||||||
buf.WriteString(fmt.Sprintf(
|
|
||||||
"\t\t\"%s\" [label=\"%s\",shape=box,style=filled,color=\"#FF0000\",fillcolor=\"#FF9494\"];\n", uniqueName, n))
|
|
||||||
|
|
||||||
graphWriteEdges(&edgeBuf, n, opts)
|
|
||||||
}
|
|
||||||
buf.WriteString("\t}\n\n")
|
|
||||||
if edgeBuf.Len() > 0 {
|
|
||||||
buf.WriteString(edgeBuf.String())
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the meta resources
|
|
||||||
/*
|
|
||||||
edgeBuf.Reset()
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
_, ok := n.Meta.(*GraphNodeResourceMeta)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine which edges to add
|
|
||||||
var edges []digraph.Edge
|
|
||||||
if hasDiff {
|
|
||||||
for _, e := range n.Edges() {
|
|
||||||
rn, ok := e.Tail().(*depgraph.Noun).Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rn.Resource.Diff == nil || rn.Resource.Diff.Empty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
edges = append(edges, e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
edges = n.Edges()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not draw if we have no edges
|
|
||||||
if len(edges) == 0 {
|
|
||||||
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",
|
|
||||||
uniqueName,
|
|
||||||
uniqueTarget))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if edgeBuf.Len() > 0 {
|
|
||||||
buf.WriteString(edgeBuf.String())
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func graphDotAddResourceProviders(
|
|
||||||
buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) {
|
|
||||||
var edgeBuf bytes.Buffer
|
|
||||||
buf.WriteString("\tsubgraph {\n")
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
_, ok := n.Meta.(*GraphNodeResourceProvider)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueName := fmt.Sprintf("%d_%s", opts.depth, n)
|
|
||||||
|
|
||||||
// Create this node.
|
|
||||||
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")
|
|
||||||
|
|
||||||
// Build up all the edges in a separate buffer so they're not in the
|
|
||||||
// subgraph.
|
|
||||||
graphWriteEdges(&edgeBuf, n, opts)
|
|
||||||
}
|
|
||||||
buf.WriteString("\t}\n\n")
|
|
||||||
if edgeBuf.Len() > 0 {
|
|
||||||
buf.WriteString(edgeBuf.String())
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func graphDotTitle(buf *bytes.Buffer, g *depgraph.Graph) {
|
|
||||||
// Determine if we have diffs. If we do, then we're graphing a
|
|
||||||
// plan, which alters our graph a bit.
|
|
||||||
hasDiff := false
|
|
||||||
for _, n := range g.Nouns {
|
|
||||||
rn, ok := n.Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() {
|
|
||||||
hasDiff = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
graphType := "Configuration"
|
|
||||||
if hasDiff {
|
|
||||||
graphType = "Plan"
|
|
||||||
}
|
|
||||||
title := fmt.Sprintf("Terraform %s Resource Graph", graphType)
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue