Remove dot package

Unify all dot functionality in the dag package
This commit is contained in:
James Bardin 2016-11-10 09:28:42 -05:00
parent 8a5d71b0ac
commit 6f347ebb3a
12 changed files with 65 additions and 352 deletions

View File

@ -5,8 +5,6 @@ import (
"fmt"
"sort"
"strings"
"github.com/hashicorp/terraform/dot"
)
// DotOpts are the options for generating a dot formatted Graph.
@ -28,13 +26,19 @@ type DotOpts struct {
// GraphNodeDotter can be implemented by a node to cause it to be included
// in the dot graph. The Dot method will be called which is expected to
// return a representation of this node.
// TODO remove the dot package dependency
type GraphNodeDotter interface {
// Dot is called to return the dot formatting for the node.
// The first parameter is the title of the node.
// The second parameter includes user-specified options that affect the dot
// graph. See GraphDotOpts below for details.
DotNode(string, *DotOpts) *dot.Node
DotNode(string, *DotOpts) *DotNode
}
// DotNode provides a structure for Vertices to return in order to specify their
// dot format.
type DotNode struct {
Name string
Attrs map[string]string
}
// Returns the DOT representation of this Graph.

View File

@ -301,11 +301,6 @@ func (g *Graph) String() string {
return buf.String()
}
func (g *Graph) Marshal() ([]byte, error) {
dg := newMarshalGraph("", g)
return json.MarshalIndent(dg, "", " ")
}
func (g *Graph) Dot(opts *DotOpts) []byte {
return newMarshalGraph("", g).Dot(opts)
}

View File

@ -9,7 +9,7 @@ import (
// the marshal* structs are for serialization of the graph data.
type marshalGraph struct {
// Each marshal structure require a unique ID so that it can be references
// Each marshal structure requires a unique ID so that it can be referenced
// by other structures.
ID string `json:",omitempty"`
@ -52,7 +52,7 @@ type marshalVertex struct {
Attrs map[string]string `json:",omitempty"`
// This is to help transition from the old Dot interfaces. We record if the
// node was a GraphNodeDotter here, so know if it should be included in the
// node was a GraphNodeDotter here, so we know if it should be included in the
// dot output
graphNodeDotter bool
}

View File

@ -1,237 +0,0 @@
// The dot package contains utilities for working with DOT graphs.
package dot
import (
"bytes"
"fmt"
"sort"
"strings"
)
// Graph is a representation of a drawable DOT graph.
type Graph struct {
// Whether this is a "digraph" or just a "graph"
Directed bool
// Used for K/V settings in the DOT
Attrs map[string]string
Nodes []*Node
Edges []*Edge
Subgraphs []*Subgraph
nodesByName map[string]*Node
}
// Subgraph is a Graph that lives inside a Parent graph, and contains some
// additional parameters to control how it is drawn.
type Subgraph struct {
Graph
Name string
Parent *Graph
Cluster bool
}
// An Edge in a DOT graph, as expressed by recording the Name of the Node at
// each end.
type Edge struct {
// Name of source node.
Source string
// Name of dest node.
Dest string
// List of K/V attributes for this edge.
Attrs map[string]string
}
// A Node in a DOT graph.
type Node struct {
Name string
Attrs map[string]string
}
// Creates a properly initialized DOT Graph.
func NewGraph(attrs map[string]string) *Graph {
return &Graph{
Attrs: attrs,
nodesByName: make(map[string]*Node),
}
}
func NewEdge(src, dst string, attrs map[string]string) *Edge {
return &Edge{
Source: src,
Dest: dst,
Attrs: attrs,
}
}
func NewNode(n string, attrs map[string]string) *Node {
return &Node{
Name: n,
Attrs: attrs,
}
}
// Initializes a Subgraph with the provided name, attaches is to this Graph,
// and returns it.
func (g *Graph) AddSubgraph(name string) *Subgraph {
subgraph := &Subgraph{
Graph: *NewGraph(map[string]string{}),
Parent: g,
Name: name,
}
g.Subgraphs = append(g.Subgraphs, subgraph)
return subgraph
}
func (g *Graph) AddAttr(k, v string) {
g.Attrs[k] = v
}
func (g *Graph) AddNode(n *Node) {
g.Nodes = append(g.Nodes, n)
g.nodesByName[n.Name] = n
}
func (g *Graph) AddEdge(e *Edge) {
g.Edges = append(g.Edges, e)
}
// Adds an edge between two Nodes.
//
// Note this does not do any verification of the existence of these nodes,
// which means that any strings you provide that are not existing nodes will
// result in extra auto-defined nodes in your resulting DOT.
func (g *Graph) AddEdgeBetween(src, dst string, attrs map[string]string) error {
g.AddEdge(NewEdge(src, dst, attrs))
return nil
}
// Look up a node by name
func (g *Graph) GetNode(name string) (*Node, error) {
node, ok := g.nodesByName[name]
if !ok {
return nil, fmt.Errorf("Could not find node: %s", name)
}
return node, nil
}
// Returns the DOT representation of this Graph.
func (g *Graph) String() string {
w := newGraphWriter()
g.drawHeader(w)
w.Indent()
g.drawBody(w)
w.Unindent()
g.drawFooter(w)
return w.String()
}
// Returns the DOT representation of this Graph.
func (g *Graph) Bytes() []byte {
w := newGraphWriter()
g.drawHeader(w)
w.Indent()
g.drawBody(w)
w.Unindent()
g.drawFooter(w)
return w.Bytes()
}
func (g *Graph) drawHeader(w *graphWriter) {
if g.Directed {
w.Printf("digraph {\n")
} else {
w.Printf("graph {\n")
}
}
func (g *Graph) drawBody(w *graphWriter) {
for _, as := range attrStrings(g.Attrs) {
w.Printf("%s\n", as)
}
nodeStrings := make([]string, 0, len(g.Nodes))
for _, n := range g.Nodes {
nodeStrings = append(nodeStrings, n.String())
}
sort.Strings(nodeStrings)
for _, ns := range nodeStrings {
w.Printf(ns)
}
edgeStrings := make([]string, 0, len(g.Edges))
for _, e := range g.Edges {
edgeStrings = append(edgeStrings, e.String())
}
sort.Strings(edgeStrings)
for _, es := range edgeStrings {
w.Printf(es)
}
for _, s := range g.Subgraphs {
s.drawHeader(w)
w.Indent()
s.drawBody(w)
w.Unindent()
s.drawFooter(w)
}
}
func (g *Graph) drawFooter(w *graphWriter) {
w.Printf("}\n")
}
// Returns the DOT representation of this Edge.
func (e *Edge) String() string {
var buf bytes.Buffer
buf.WriteString(
fmt.Sprintf(
"%q -> %q", e.Source, e.Dest))
writeAttrs(&buf, e.Attrs)
buf.WriteString("\n")
return buf.String()
}
func (s *Subgraph) drawHeader(w *graphWriter) {
name := s.Name
if s.Cluster {
name = fmt.Sprintf("cluster_%s", name)
}
w.Printf("subgraph %q {\n", name)
}
// Returns the DOT representation of this Node.
func (n *Node) String() string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("%q", n.Name))
writeAttrs(&buf, n.Attrs)
buf.WriteString("\n")
return buf.String()
}
func writeAttrs(buf *bytes.Buffer, attrs map[string]string) {
if len(attrs) > 0 {
buf.WriteString(" [")
buf.WriteString(strings.Join(attrStrings(attrs), ", "))
buf.WriteString("]")
}
}
func attrStrings(attrs map[string]string) []string {
strings := make([]string, 0, len(attrs))
for k, v := range attrs {
strings = append(strings, fmt.Sprintf("%s = %q", k, v))
}
sort.Strings(strings)
return strings
}

View File

@ -1,47 +0,0 @@
package dot
import (
"bytes"
"fmt"
)
// graphWriter wraps a bytes.Buffer and tracks indent level levels.
type graphWriter struct {
bytes.Buffer
indent int
indentStr string
}
// Returns an initialized graphWriter at indent level 0.
func newGraphWriter() *graphWriter {
w := &graphWriter{
indent: 0,
}
w.init()
return w
}
// Prints to the buffer at the current indent level.
func (w *graphWriter) Printf(s string, args ...interface{}) {
w.WriteString(w.indentStr + fmt.Sprintf(s, args...))
}
// Increase the indent level.
func (w *graphWriter) Indent() {
w.indent++
w.init()
}
// Decrease the indent level.
func (w *graphWriter) Unindent() {
w.indent--
w.init()
}
func (w *graphWriter) init() {
indentBuf := new(bytes.Buffer)
for i := 0; i < w.indent; i++ {
indentBuf.WriteString("\t")
}
w.indentStr = indentBuf.String()
}

View File

@ -7,7 +7,6 @@ import (
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeConfigModule represents a module within the configuration graph.
@ -129,11 +128,14 @@ func (n *graphNodeModuleExpanded) DependentOn() []string {
}
// GraphNodeDotter impl.
func (n *graphNodeModuleExpanded) DotNode(name string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(name, map[string]string{
"label": dag.VertexName(n.Original),
"shape": "component",
})
func (n *graphNodeModuleExpanded) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": dag.VertexName(n.Original),
"shape": "component",
},
}
}
// GraphNodeEvalable impl.

View File

@ -5,7 +5,6 @@ import (
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeConfigProvider represents a configured provider within the
@ -59,11 +58,14 @@ func (n *GraphNodeConfigProvider) ProviderConfig() *config.RawConfig {
}
// GraphNodeDotter impl.
func (n *GraphNodeConfigProvider) DotNode(name string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(name, map[string]string{
"label": n.Name(),
"shape": "diamond",
})
func (n *GraphNodeConfigProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "diamond",
},
}
}
// GraphNodeDotterOrigin impl.

View File

@ -7,7 +7,6 @@ import (
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeCountDependent is implemented by resources for giving only
@ -128,14 +127,17 @@ func (n *GraphNodeConfigResource) Name() string {
}
// GraphNodeDotter impl.
func (n *GraphNodeConfigResource) DotNode(name string, opts *dag.DotOpts) *dot.Node {
func (n *GraphNodeConfigResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
if n.Destroy && !opts.Verbose {
return nil
}
return dot.NewNode(name, map[string]string{
"label": n.Name(),
"shape": "box",
})
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "box",
},
}
}
// GraphNodeFlattenable impl.

View File

@ -1,20 +1,6 @@
package terraform
import (
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeDotter can be implemented by a node to cause it to be included
// in the dot graph. The Dot method will be called which is expected to
// return a representation of this node.
type GraphNodeDotter interface {
// Dot is called to return the dot formatting for the node.
// The first parameter is the title of the node.
// The second parameter includes user-specified options that affect the dot
// graph. See GraphDotOpts below for details.
DotNode(string, *dag.DotOpts) *dot.Node
}
import "github.com/hashicorp/terraform/dag"
// GraphDot returns the dot formatting of a visual representation of
// the given Terraform graph.

View File

@ -5,7 +5,6 @@ import (
"testing"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
func TestGraphDot(t *testing.T) {
@ -263,8 +262,8 @@ type testDrawable struct {
func (node *testDrawable) Name() string {
return node.VertexName
}
func (node *testDrawable) DotNode(n string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(n, map[string]string{})
func (node *testDrawable) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{Name: n, Attrs: map[string]string{}}
}
func (node *testDrawable) DependableName() []string {
return []string{node.VertexName}
@ -280,8 +279,8 @@ type testDrawableOrigin struct {
func (node *testDrawableOrigin) Name() string {
return node.VertexName
}
func (node *testDrawableOrigin) DotNode(n string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(n, map[string]string{})
func (node *testDrawableOrigin) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{Name: n, Attrs: map[string]string{}}
}
func (node *testDrawableOrigin) DotOrigin() bool {
return true
@ -302,8 +301,8 @@ func (node *testDrawableSubgraph) Name() string {
func (node *testDrawableSubgraph) Subgraph() dag.Grapher {
return node.SubgraphMock
}
func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(n, map[string]string{})
func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{Name: n, Attrs: map[string]string{}}
}
func (node *testDrawableSubgraph) DependentOn() []string {
return node.DependentOnMock

View File

@ -8,7 +8,6 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeProvider is an interface that nodes that can be a provider
@ -355,14 +354,17 @@ func (n *graphNodeCloseProvider) CloseProviderName() string {
}
// GraphNodeDotter impl.
func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dot.Node {
func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
if !opts.Verbose {
return nil
}
return dot.NewNode(name, map[string]string{
"label": n.Name(),
"shape": "diamond",
})
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "diamond",
},
}
}
type graphNodeProvider struct {
@ -393,11 +395,14 @@ func (n *graphNodeProvider) ProviderConfig() *config.RawConfig {
}
// GraphNodeDotter impl.
func (n *graphNodeProvider) DotNode(name string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(name, map[string]string{
"label": n.Name(),
"shape": "diamond",
})
func (n *graphNodeProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "diamond",
},
}
}
// GraphNodeDotterOrigin impl.

View File

@ -5,7 +5,6 @@ import (
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// DisableProviderTransformer "disables" any providers that are only
@ -102,11 +101,14 @@ func (n *graphNodeDisabledProvider) Name() string {
}
// GraphNodeDotter impl.
func (n *graphNodeDisabledProvider) DotNode(name string, opts *dag.DotOpts) *dot.Node {
return dot.NewNode(name, map[string]string{
"label": n.Name(),
"shape": "diamond",
})
func (n *graphNodeDisabledProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "diamond",
},
}
}
// GraphNodeDotterOrigin impl.