func (w *ContextGraphWalker) ExitEvalTree( v dag.Vertex, output interface{}, err error) error { // Release the semaphore w.Context.parallelSem.Release() if err == nil { return nil } // Acquire the lock because anything is going to require a lock. w.errorLock.Lock() defer w.errorLock.Unlock() // Try to get a validation error out of it. If its not a validation // error, then just record the normal error. verr, ok := err.(*EvalValidateError) if !ok { return err } for _, msg := range verr.Warnings { w.ValidationWarnings = append( w.ValidationWarnings, fmt.Sprintf("%s: %s", dag.VertexName(v), msg)) } for _, e := range verr.Errors { w.ValidationErrors = append( w.ValidationErrors, errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e)) } return nil }
func (t *VertexTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { for _, vt := range t.Transforms { newV, err := vt.Transform(v) if err != nil { return err } // If the vertex didn't change, then don't do anything more if newV == v { continue } // Vertex changed, replace it within the graph if ok := g.Replace(v, newV); !ok { // This should never happen, big problem return fmt.Errorf( "Failed to replace %s with %s!\n\nSource: %#v\n\nTarget: %#v", dag.VertexName(v), dag.VertexName(newV), v, newV) } // Replace v so that future transforms use the proper vertex v = newV } } return nil }
func (t *ExpandTransform) Transform(v dag.Vertex) (dag.Vertex, error) { ev, ok := v.(GraphNodeExpandable) if !ok { // This isn't an expandable vertex, so just ignore it. return v, nil } // Expand the subgraph! log.Printf("[DEBUG] vertex %s: static expanding", dag.VertexName(ev)) return ev.Expand(t.Builder) }
// GraphNodeDestroyable impl. func (n *GraphNodeConfigResourceFlat) DestroyNode(mode GraphNodeDestroyMode) GraphNodeDestroy { // Get our parent destroy node. If we don't have any, just return raw := n.GraphNodeConfigResource.DestroyNode(mode) if raw == nil { return nil } node, ok := raw.(*graphNodeResourceDestroy) if !ok { panic(fmt.Sprintf("unknown destroy node: %s %T", dag.VertexName(raw), raw)) } // Otherwise, wrap it so that it gets the proper module treatment. return &graphNodeResourceDestroyFlat{ graphNodeResourceDestroy: node, PathValue: n.PathValue, FlatCreateNode: n, } }
func (t *DisableProviderTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { // We only care about providers pn, ok := v.(GraphNodeProvider) if !ok || pn.ProviderName() == "" { continue } // Go through all the up-edges (things that depend on this // provider) and if any is not a module, then ignore this node. nonModule := false for _, sourceRaw := range g.UpEdges(v).List() { source := sourceRaw.(dag.Vertex) cn, ok := source.(graphNodeConfig) if !ok { nonModule = true break } if cn.ConfigType() != GraphNodeConfigTypeModule { nonModule = true break } } if nonModule { // We found something that depends on this provider that // isn't a module, so skip it. continue } // Disable the provider by replacing it with a "disabled" provider disabled := &graphNodeDisabledProvider{GraphNodeProvider: pn} if !g.Replace(v, disabled) { panic(fmt.Sprintf( "vertex disappeared from under us: %s", dag.VertexName(v))) } } return nil }
func (t *ProvisionerTransformer) Transform(g *Graph) error { // Go through the other nodes and match them to provisioners they need var err error m := provisionerVertexMap(g) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProvisionerConsumer); ok { for _, provisionerName := range pv.ProvisionedBy() { target := m[provisionerName] if target == nil { err = multierror.Append(err, fmt.Errorf( "%s: provisioner %s couldn't be found", dag.VertexName(v), provisionerName)) continue } g.Connect(dag.BasicEdge(v, target)) } } } return err }
func (g *Graph) walk(walker GraphWalker) error { // The callbacks for enter/exiting a graph ctx := walker.EnterPath(g.Path) defer walker.ExitPath(g.Path) // Get the path for logs path := strings.Join(ctx.Path(), ".") // Walk the graph. var walkFn dag.WalkFunc walkFn = func(v dag.Vertex) (rerr error) { log.Printf("[DEBUG] vertex %s.%s: walking", path, dag.VertexName(v)) walker.EnterVertex(v) defer func() { walker.ExitVertex(v, rerr) }() // vertexCtx is the context that we use when evaluating. This // is normally the context of our graph but can be overridden // with a GraphNodeSubPath impl. vertexCtx := ctx if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { vertexCtx = walker.EnterPath(pn.Path()) defer walker.ExitPath(pn.Path()) } // If the node is eval-able, then evaluate it. if ev, ok := v.(GraphNodeEvalable); ok { tree := ev.EvalTree() if tree == nil { panic(fmt.Sprintf( "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v)) } // Allow the walker to change our tree if needed. Eval, // then callback with the output. log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v)) tree = walker.EnterEvalTree(v, tree) output, err := Eval(tree, vertexCtx) if rerr = walker.ExitEvalTree(v, output, err); rerr != nil { return } } // If the node is dynamically expanded, then expand it if ev, ok := v.(GraphNodeDynamicExpandable); ok { log.Printf( "[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph", path, dag.VertexName(v)) g, err := ev.DynamicExpand(vertexCtx) if err != nil { rerr = err return } // Walk the subgraph if rerr = g.walk(walker); rerr != nil { return } } // If the node has a subgraph, then walk the subgraph if sn, ok := v.(GraphNodeSubgraph); ok { log.Printf( "[DEBUG] vertex %s.%s: walking subgraph", path, dag.VertexName(v)) if rerr = sn.Subgraph().walk(walker); rerr != nil { return } } return nil } return g.AcyclicGraph.Walk(walkFn) }
func graphDotSubgraph( dg *dot.Graph, modName string, g *Graph, opts *GraphDotOpts, modDepth int) error { // Respect user-specified module depth if opts.MaxDepth >= 0 && modDepth > opts.MaxDepth { return nil } // Begin module subgraph var sg *dot.Subgraph if modDepth == 0 { sg = dg.AddSubgraph(modName) } else { sg = dg.AddSubgraph(modName) sg.Cluster = true sg.AddAttr("label", modName) } origins, err := graphDotFindOrigins(g) if err != nil { return err } drawableVertices := make(map[dag.Vertex]struct{}) toDraw := make([]dag.Vertex, 0, len(g.Vertices())) subgraphVertices := make(map[dag.Vertex]*Graph) walk := func(v dag.Vertex, depth int) error { // We only care about nodes that yield non-empty Dot strings. if dn, ok := v.(GraphNodeDotter); !ok { return nil } else if dn.DotNode("fake", opts) == nil { return nil } drawableVertices[v] = struct{}{} toDraw = append(toDraw, v) if sn, ok := v.(GraphNodeSubgraph); ok { subgraphVertices[v] = sn.Subgraph() } return nil } if err := g.ReverseDepthFirstWalk(origins, walk); err != nil { return err } for _, v := range toDraw { dn := v.(GraphNodeDotter) nodeName := graphDotNodeName(modName, v) sg.AddNode(dn.DotNode(nodeName, opts)) // Draw all the edges from this vertex to other nodes targets := dag.AsVertexList(g.DownEdges(v)) for _, t := range targets { target := t.(dag.Vertex) // Only want edges where both sides are drawable. if _, ok := drawableVertices[target]; !ok { continue } if err := sg.AddEdgeBetween( graphDotNodeName(modName, v), graphDotNodeName(modName, target), map[string]string{}); err != nil { return err } } } // Recurse into any subgraphs for _, v := range toDraw { subgraph, ok := subgraphVertices[v] if !ok { continue } err := graphDotSubgraph(dg, dag.VertexName(v), subgraph, opts, modDepth+1) if err != nil { return err } } if opts.DrawCycles { colors := []string{"red", "green", "blue"} for ci, cycle := range g.Cycles() { for i, c := range cycle { // Catch the last wrapping edge of the cycle if i+1 >= len(cycle) { i = -1 } edgeAttrs := map[string]string{ "color": colors[ci%len(colors)], "penwidth": "2.0", } if err := sg.AddEdgeBetween( graphDotNodeName(modName, c), graphDotNodeName(modName, cycle[i+1]), edgeAttrs); err != nil { return err } } } } return nil }
func graphDotNodeName(modName, v dag.Vertex) string { return fmt.Sprintf("[%s] %s", modName, dag.VertexName(v)) }
// GraphNodeDotter impl. func (n *graphNodeModuleExpanded) DotNode(name string, opts *GraphDotOpts) *dot.Node { return dot.NewNode(name, map[string]string{ "label": dag.VertexName(n.Original), "shape": "component", }) }
func (n *graphNodeModuleExpanded) Name() string { return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original)) }
func (n *graphNodeDisabledProvider) Name() string { return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider)) }