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
}
Example #2
0
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
}
Example #3
0
// testGraphHappensBefore is an assertion helper that tests that node
// A (dag.VertexName value) happens before node B.
func testGraphHappensBefore(t *testing.T, g *Graph, A, B string) {
	// Find the B vertex
	var vertexB dag.Vertex
	for _, v := range g.Vertices() {
		if dag.VertexName(v) == B {
			vertexB = v
			break
		}
	}
	if vertexB == nil {
		t.Fatalf(
			"Expected %q before %q. Couldn't find %q in:\n\n%s",
			A, B, B, g.String())
	}

	// Look at ancestors
	deps, err := g.Ancestors(vertexB)
	if err != nil {
		t.Fatalf("Error: %s in graph:\n\n%s", err, g.String())
	}

	// Make sure B is in there
	for _, v := range deps.List() {
		if dag.VertexName(v) == A {
			// Success
			return
		}
	}

	t.Fatalf(
		"Expected %q before %q in:\n\n%s",
		A, B, g.String())
}
// GraphNodeNoopPrunable
func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool {
	log.Printf("[DEBUG] Checking variable noop: %s", n.Name())
	// 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() {
		log.Printf("[DEBUG] No diff, not a noop")
		return false
	}

	// We have to find our our module diff since we do funky things with
	// the flat node's implementation of Path() below.
	modDiff := opts.Diff.ModuleByPath(n.ModulePath)

	// If we're destroying, we have no need of variables.
	if modDiff != nil && modDiff.Destroy {
		log.Printf("[DEBUG] Destroy diff, treating variable as a noop")
		return true
	}

	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
		}

		log.Printf("[DEBUG] Found up edge to %s, var is not noop", dag.VertexName(v))
		return false
	}

	log.Printf("[DEBUG] No up edges, treating variable as a noop")
	return true
}
Example #5
0
func (c *Core) walk(f func(app.App, *app.Context, bool) error) error {
	root, err := c.appfileCompiled.Graph.Root()
	if err != nil {
		return fmt.Errorf(
			"Error loading app: %s", err)
	}

	// Walk the appfile graph.
	var stop int32 = 0
	return c.appfileCompiled.Graph.Walk(func(raw dag.Vertex) (err error) {
		// If we're told to stop (something else had an error), then stop early.
		// Graphs walks by default will complete all disjoint parts of the
		// graph before failing, but Otto doesn't have to do that.
		if atomic.LoadInt32(&stop) != 0 {
			return nil
		}

		// If we exit with an error, then mark the stop atomic
		defer func() {
			if err != nil {
				atomic.StoreInt32(&stop, 1)
			}
		}()

		// Convert to the rich vertex type so that we can access data
		v := raw.(*appfile.CompiledGraphVertex)

		// Do some logging to help ourselves out
		log.Printf("[DEBUG] core walking app: %s", v.File.Application.Name)

		// Get the context and app for this appfile
		appCtx, err := c.appContext(v.File)
		if err != nil {
			return fmt.Errorf(
				"Error loading Appfile for '%s': %s",
				dag.VertexName(raw), err)
		}
		app, err := c.app(appCtx)
		if err != nil {
			return fmt.Errorf(
				"Error loading App implementation for '%s': %s",
				dag.VertexName(raw), err)
		}
		defer maybeClose(app)

		// Call our callback
		return f(app, appCtx, raw == root)
	})
}
Example #6
0
func (t *TargetsTransformer) Transform(g *Graph) error {
	if len(t.Targets) > 0 && len(t.ParsedTargets) == 0 {
		addrs, err := t.parseTargetAddresses()
		if err != nil {
			return err
		}
		t.ParsedTargets = addrs
	}
	if len(t.ParsedTargets) > 0 {
		targetedNodes, err := t.selectTargetedNodes(g, t.ParsedTargets)
		if err != nil {
			return err
		}

		for _, v := range g.Vertices() {
			removable := false
			if _, ok := v.(GraphNodeAddressable); ok {
				removable = true
			}
			if vr, ok := v.(RemovableIfNotTargeted); ok {
				removable = vr.RemoveIfNotTargeted()
			}
			if removable && !targetedNodes.Include(v) {
				log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
				g.Remove(v)
			}
		}
	}
	return nil
}
Example #7
0
// GraphDot returns the dot formatting of a visual representation of
// the given Terraform graph.
func GraphDot(g *Graph, opts *GraphDotOpts) string {
	buf := new(bytes.Buffer)

	// Start the graph
	buf.WriteString("digraph {\n")
	buf.WriteString("\tcompound = true;\n")

	// Go through all the vertices and draw it
	vertices := g.Vertices()
	dotVertices := make(map[dag.Vertex]struct{}, len(vertices))
	for _, v := range vertices {
		if dn, ok := v.(GraphNodeDotter); !ok {
			continue
		} else if dn.Dot("fake") == "" {
			continue
		}

		dotVertices[v] = struct{}{}
	}

	for v, _ := range dotVertices {
		dn := v.(GraphNodeDotter)
		scanner := bufio.NewScanner(strings.NewReader(
			dn.Dot(dag.VertexName(v))))
		for scanner.Scan() {
			buf.WriteString("\t" + scanner.Text() + "\n")
		}

		// Draw all the edges
		for _, t := range g.DownEdges(v).List() {
			target := t.(dag.Vertex)
			if _, ok := dotVertices[target]; !ok {
				continue
			}

			buf.WriteString(fmt.Sprintf(
				"\t\"%s\" -> \"%s\";\n",
				dag.VertexName(v),
				dag.VertexName(target)))
		}
	}

	// End the graph
	buf.WriteString("}\n")
	return buf.String()
}
// GraphNodeDotter impl.
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",
		},
	}
}
Example #9
0
// GraphNodeDotter impl.
func (n *graphNodeModuleExpanded) Dot(name string) string {
	return fmt.Sprintf(
		"\"%s\" [\n"+
			"\tlabel=\"%s\"\n"+
			"\tshape=component\n"+
			"];",
		name,
		dag.VertexName(n.Original))
}
Example #10
0
func (c *Core) walk(f func(app.App, *app.Context, bool) error) error {
	root, err := c.appfileCompiled.Graph.Root()
	if err != nil {
		return fmt.Errorf("装载App报错: %s", err)
	}

	//Walk the appfile graph
	var stop int32 = 0
	return c.appfileCompiled.Graph.Walk(func(raw dag.Vertex) (err error) {
		// 如果stop(发生一些错误),那么尽早stop.
		// If we're told to stop (something else had an error), then stop early.
		// Graphs walks by default will complete all disjoint parts of the
		// graph before failing, but Otto doesn't have to do that.
		if atomic.LoadInt32(&stop) != 0 {
			return nil
		}

		//如果报错退出,我们标记stop atomic
		defer func() {
			if err != nil {
				atomic.StoreInt32(&stop, 1)
			}
		}()

		// 转换至丰富的Vertex以便我们能够访问数据
		v := raw.(*appfile.CompiledGraphVertex)

		// 给appfile获取App上下文
		appCtx, err := c.appContext(v.File)
		if err != nil {
			return fmt.Errorf(
				"loading Appfile for '%s': %s 报错", dag.VertexName(raw), err)
		}

		app, err := c.app(appCtx)
		if err != nil {
			return fmt.Errorf(
				"获取App实现报错 '%s': %s", dag.VertexName(raw), err)
		}

		// 执行回调
		return f(app, appCtx, raw == root)
	})
}
func (t *AttachStateTransformer) Transform(g *Graph) error {
	// If no state, then nothing to do
	if t.State == nil {
		log.Printf("[DEBUG] Not attaching any state: state is nil")
		return nil
	}

	filter := &StateFilter{State: t.State}
	for _, v := range g.Vertices() {
		// Only care about nodes requesting we're adding state
		an, ok := v.(GraphNodeAttachResourceState)
		if !ok {
			continue
		}
		addr := an.ResourceAddr()

		// Get the module state
		results, err := filter.Filter(addr.String())
		if err != nil {
			return err
		}

		// Attach the first resource state we get
		found := false
		for _, result := range results {
			if rs, ok := result.Value.(*ResourceState); ok {
				log.Printf(
					"[DEBUG] Attaching resource state to %q: %s",
					dag.VertexName(v), rs)
				an.AttachResourceState(rs)
				found = true
				break
			}
		}

		if !found {
			log.Printf(
				"[DEBUG] Resource state not found for %q: %s",
				dag.VertexName(v), addr)
		}
	}

	return nil
}
Example #12
0
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))
}
Example #13
0
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 %q: static expanding", dag.VertexName(ev))
	return ev.Expand(t.Builder)
}
// GraphNodeDestroyEdgeInclude impl.
func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool {
	// Only include this variable in a destroy edge if the source vertex
	// "v" has a count dependency on this variable.
	log.Printf("[DEBUG] DestroyEdgeInclude: Checking: %s", dag.VertexName(v))
	cv, ok := v.(GraphNodeCountDependent)
	if !ok {
		log.Printf("[DEBUG] DestroyEdgeInclude: Not GraphNodeCountDependent: %s", dag.VertexName(v))
		return false
	}

	for _, d := range cv.CountDependentOn() {
		for _, d2 := range n.DependableName() {
			log.Printf("[DEBUG] DestroyEdgeInclude: d = %s : d2 = %s", d, d2)
			if d == d2 {
				return true
			}
		}
	}

	return false
}
Example #15
0
func (t *ReferenceTransformer) Transform(g *Graph) error {
	// Build a reference map so we can efficiently look up the references
	vs := g.Vertices()
	m := NewReferenceMap(vs)

	// Find the things that reference things and connect them
	for _, v := range vs {
		parents, _ := m.References(v)
		parentsDbg := make([]string, len(parents))
		for i, v := range parents {
			parentsDbg[i] = dag.VertexName(v)
		}
		log.Printf(
			"[DEBUG] ReferenceTransformer: %q references: %v",
			dag.VertexName(v), parentsDbg)

		for _, parent := range parents {
			g.Connect(dag.BasicEdge(v, parent))
		}
	}

	return nil
}
// hasDestroyEdgeInPath recursively walks for a destroy edge, ensuring that
// a variable both has no immediate destroy edges or any in its full module
// path, ensuring that links do not get severed in the middle.
func (n *GraphNodeConfigVariable) hasDestroyEdgeInPath(opts *NoopOpts, vertex dag.Vertex) bool {
	if vertex == nil {
		vertex = opts.Vertex
	}

	log.Printf("[DEBUG] hasDestroyEdgeInPath: Looking for destroy edge: %s - %T", dag.VertexName(vertex), vertex)
	for _, v := range opts.Graph.UpEdges(vertex).List() {
		if len(opts.Graph.UpEdges(v).List()) > 1 {
			if n.hasDestroyEdgeInPath(opts, v) == true {
				return true
			}
		}

		// Here we borrow the implementation of DestroyEdgeInclude, whose logic
		// and semantics are exactly what we want here. We add a check for the
		// the root node, since we have to always depend on its existance.
		if cv, ok := vertex.(*GraphNodeConfigVariableFlat); ok {
			if dag.VertexName(v) == rootNodeName || cv.DestroyEdgeInclude(v) {
				return true
			}
		}
	}
	return false
}
func TestReferenceMapReferencedBy(t *testing.T) {
	cases := map[string]struct {
		Nodes  []dag.Vertex
		Check  dag.Vertex
		Result []string
	}{
		"simple": {
			Nodes: []dag.Vertex{
				&graphNodeRefChildTest{
					NameValue: "A",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "B",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "C",
					Refs:      []string{"B"},
				},
			},
			Check: &graphNodeRefParentTest{
				NameValue: "foo",
				Names:     []string{"A"},
			},
			Result: []string{"A", "B"},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			rm := NewReferenceMap(tc.Nodes)
			result := rm.ReferencedBy(tc.Check)

			var resultStr []string
			for _, v := range result {
				resultStr = append(resultStr, dag.VertexName(v))
			}

			sort.Strings(resultStr)
			sort.Strings(tc.Result)
			if !reflect.DeepEqual(resultStr, tc.Result) {
				t.Fatalf("bad: %#v", resultStr)
			}
		})
	}
}
func (t *DisableProviderTransformerOld) Transform(g *Graph) error {
	// Since we're comparing against edges, we need to make sure we connect
	g.ConnectDependents()

	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
}
// 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 *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 _, p := range pv.ProvisionedBy() {
				if m[p] == nil {
					err = multierror.Append(err, fmt.Errorf(
						"%s: provisioner %s couldn't be found",
						dag.VertexName(v), p))
					continue
				}

				g.Connect(dag.BasicEdge(v, m[p]))
			}
		}
	}

	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() {
				key := p
				source := cpm[key]

				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[key]
					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[key] = source
				}

				// Close node depends on all nodes provided by the provider
				g.Connect(dag.BasicEdge(source, v))
			}
		}
	}

	return err
}
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[providerMapKey(p, pv)]
				if target == nil {
					println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv)))
					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 *DisableProviderTransformer) Transform(g *Graph) error {
	for _, v := range g.Vertices() {
		// We only care about providers
		pn, ok := v.(GraphNodeProvider)
		if !ok || pn.ProviderName() == "" {
			continue
		}

		// If we have dependencies, then don't disable
		if g.UpEdges(v).Len() > 0 {
			continue
		}

		// Get the path
		var path []string
		if pn, ok := v.(GraphNodeSubPath); ok {
			path = pn.Path()
		}

		// Disable the provider by replacing it with a "disabled" provider
		disabled := &NodeDisabledProvider{
			NodeAbstractProvider: &NodeAbstractProvider{
				NameValue: pn.ProviderName(),
				PathValue: path,
			},
		}

		if !g.Replace(v, disabled) {
			panic(fmt.Sprintf(
				"vertex disappeared from under us: %s",
				dag.VertexName(v)))
		}
	}

	return nil
}
Example #25
0
func (c *Compiled) Validate() error {
	var result error

	// First validate that there are no cycles in the dependency graph
	if cycles := c.Graph.Cycles(); len(cycles) > 0 {
		for _, cycle := range cycles {
			vertices := make([]string, len(cycle))
			for i, v := range cycle {
				vertices[i] = dag.VertexName(v)
			}

			result = multierror.Append(result, fmt.Errorf(
				"Dependency cycle: %s", strings.Join(vertices, ", ")))
		}
	}

	// Validate all the files
	var errLock sync.Mutex
	c.Graph.Walk(func(raw dag.Vertex) error {
		v := raw.(*CompiledGraphVertex)
		if err := v.File.Validate(); err != nil {
			errLock.Lock()
			defer errLock.Unlock()

			if s := v.File.Source; s != "" {
				err = multierror.Prefix(err, fmt.Sprintf("Dependency %s:", s))
			}

			result = multierror.Append(result, err)
		}

		return nil
	})

	return result
}
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
}
// 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))
}
Example #29
0
func (n *graphNodeDisabledProvider) Name() string {
	return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider))
}
Example #30
0
func graphDotNodeName(modName, v dag.Vertex) string {
	return fmt.Sprintf("[%s] %s", modName, dag.VertexName(v))
}