func (w *ContextGraphWalker) ExitEvalTree( v dag.Vertex, output interface{}, err error) error { log.Printf("[TRACE] Exiting eval tree: %s", dag.VertexName(v)) // 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 (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { log.Printf("[TRACE] Entering eval tree: %s", dag.VertexName(v)) // Acquire a lock on the semaphore w.Context.parallelSem.Acquire() // We want to filter the evaluation tree to only include operations // that belong in this operation. return EvalFilter(n, EvalNodeFilterOp(w.Operation)) }
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, } }
// GraphNodeNoopPrunable func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool { // If we have no diff, always keep this in the graph. We have to do // this primarily for validation: we want to validate that variable // interpolations are valid even if there are no resources that // depend on them. if opts.Diff == nil || opts.Diff.Empty() { return false } for _, v := range opts.Graph.UpEdges(opts.Vertex).List() { // This is terrible, but I can't think of a better way to do this. if dag.VertexName(v) == rootNodeName { continue } return false } return true }
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 *ProviderTransformer) Transform(g *Graph) error { // Go through the other nodes and match them to providers they need var err error m := providerVertexMap(g) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProviderConsumer); ok { for _, p := range pv.ProvidedBy() { target := m[p] if target == nil { err = multierror.Append(err, fmt.Errorf( "%s: provider %s couldn't be found", dag.VertexName(v), p)) continue } g.Connect(dag.BasicEdge(v, target)) } } } return err }
func (t *CloseProviderTransformer) Transform(g *Graph) error { pm := providerVertexMap(g) cpm := closeProviderVertexMap(g) var err error for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProviderConsumer); ok { for _, p := range pv.ProvidedBy() { source := cpm[p] if source == nil { // Create a new graphNodeCloseProvider and add it to the graph source = &graphNodeCloseProvider{ProviderNameValue: p} g.Add(source) // Close node needs to depend on provider provider, ok := pm[p] if !ok { err = multierror.Append(err, fmt.Errorf( "%s: provider %s couldn't be found for closing", dag.VertexName(v), p)) continue } g.Connect(dag.BasicEdge(source, provider)) // Make sure we also add the new graphNodeCloseProvider to the map // so we don't create and add any duplicate graphNodeCloseProviders. cpm[p] = source } // Close node depends on all nodes provided by the provider g.Connect(dag.BasicEdge(source, v)) } } } return err }
func (t *TargetsTransformer) Transform(g *Graph) error { if len(t.Targets) > 0 { // TODO: duplicated in OrphanTransformer; pull up parsing earlier addrs, err := t.parseTargetAddresses() if err != nil { return err } targetedNodes, err := t.selectTargetedNodes(g, addrs) if err != nil { return err } for _, v := range g.Vertices() { if _, ok := v.(GraphNodeAddressable); ok { if !targetedNodes.Include(v) { log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) g.Remove(v) } } } } return nil }
func (n *graphNodeDisabledProvider) Name() string { return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider)) }
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 (t *FlattenTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { fn, ok := v.(GraphNodeFlatGraph) if !ok { continue } // If we don't want to be flattened, don't do it subgraph := fn.FlattenGraph() if subgraph == nil { continue } // Get all the things that depend on this node. We'll re-connect // dependents later. We have to copy these here since the UpEdges // value will be deleted after the Remove below. dependents := make([]dag.Vertex, 0, 5) for _, v := range g.UpEdges(v).List() { dependents = append(dependents, v) } // Remove the old node g.Remove(v) // Go through the subgraph and flatten all the nodes for _, sv := range subgraph.Vertices() { // If the vertex already has a subpath then we assume it has // already been flattened. Ignore it. if _, ok := sv.(GraphNodeSubPath); ok { continue } fn, ok := sv.(GraphNodeFlattenable) if !ok { return fmt.Errorf( "unflattenable node: %s %T", dag.VertexName(sv), sv) } v, err := fn.Flatten(subgraph.Path) if err != nil { return fmt.Errorf( "error flattening %s (%T): %s", dag.VertexName(sv), sv, err) } if v == nil { subgraph.Remove(v) } else { subgraph.Replace(sv, v) } } // Now that we've handled any changes to the graph that are // needed, we can add them all to our graph along with their edges. for _, sv := range subgraph.Vertices() { g.Add(sv) } for _, se := range subgraph.Edges() { g.Connect(se) } // Connect the dependencies for all the new nodes that we added. // This will properly connect variables to their sources, for example. for _, sv := range subgraph.Vertices() { g.ConnectDependent(sv) } // Re-connect all the things that dependent on the graph // we just flattened. This should connect them back into the // correct nodes if their DependentOn() is setup correctly. for _, v := range dependents { g.ConnectDependent(v) } } return nil }
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) }