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
}
Exemple #17
0
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)
}