github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/durgaform/graph.go (about)

     1  package durgaform
     2  
     3  import (
     4  	"log"
     5  	"strings"
     6  
     7  	"github.com/eliastor/durgaform/internal/logging"
     8  	"github.com/eliastor/durgaform/internal/tfdiags"
     9  
    10  	"github.com/eliastor/durgaform/internal/addrs"
    11  
    12  	"github.com/eliastor/durgaform/internal/dag"
    13  )
    14  
    15  // Graph represents the graph that Durgaform uses to represent resources
    16  // and their dependencies.
    17  type Graph struct {
    18  	// Graph is the actual DAG. This is embedded so you can call the DAG
    19  	// methods directly.
    20  	dag.AcyclicGraph
    21  
    22  	// Path is the path in the module tree that this Graph represents.
    23  	Path addrs.ModuleInstance
    24  }
    25  
    26  func (g *Graph) DirectedGraph() dag.Grapher {
    27  	return &g.AcyclicGraph
    28  }
    29  
    30  // Walk walks the graph with the given walker for callbacks. The graph
    31  // will be walked with full parallelism, so the walker should expect
    32  // to be called in concurrently.
    33  func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics {
    34  	return g.walk(walker)
    35  }
    36  
    37  func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {
    38  	// The callbacks for enter/exiting a graph
    39  	ctx := walker.EvalContext()
    40  
    41  	// Walk the graph.
    42  	walkFn := func(v dag.Vertex) (diags tfdiags.Diagnostics) {
    43  		// the walkFn is called asynchronously, and needs to be recovered
    44  		// separately in the case of a panic.
    45  		defer logging.PanicHandler()
    46  
    47  		log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v)
    48  
    49  		defer func() {
    50  			if diags.HasErrors() {
    51  				for _, diag := range diags {
    52  					if diag.Severity() == tfdiags.Error {
    53  						desc := diag.Description()
    54  						log.Printf("[ERROR] vertex %q error: %s", dag.VertexName(v), desc.Summary)
    55  					}
    56  				}
    57  				log.Printf("[TRACE] vertex %q: visit complete, with errors", dag.VertexName(v))
    58  			} else {
    59  				log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v))
    60  			}
    61  		}()
    62  
    63  		// vertexCtx is the context that we use when evaluating. This
    64  		// is normally the context of our graph but can be overridden
    65  		// with a GraphNodeModuleInstance impl.
    66  		vertexCtx := ctx
    67  		if pn, ok := v.(GraphNodeModuleInstance); ok {
    68  			vertexCtx = walker.EnterPath(pn.Path())
    69  			defer walker.ExitPath(pn.Path())
    70  		}
    71  
    72  		// If the node is exec-able, then execute it.
    73  		if ev, ok := v.(GraphNodeExecutable); ok {
    74  			diags = diags.Append(walker.Execute(vertexCtx, ev))
    75  			if diags.HasErrors() {
    76  				return
    77  			}
    78  		}
    79  
    80  		// If the node is dynamically expanded, then expand it
    81  		if ev, ok := v.(GraphNodeDynamicExpandable); ok {
    82  			log.Printf("[TRACE] vertex %q: expanding dynamic subgraph", dag.VertexName(v))
    83  
    84  			g, err := ev.DynamicExpand(vertexCtx)
    85  			if err != nil {
    86  				diags = diags.Append(err)
    87  				return
    88  			}
    89  			if g != nil {
    90  				// Walk the subgraph
    91  				log.Printf("[TRACE] vertex %q: entering dynamic subgraph", dag.VertexName(v))
    92  				subDiags := g.walk(walker)
    93  				diags = diags.Append(subDiags)
    94  				if subDiags.HasErrors() {
    95  					var errs []string
    96  					for _, d := range subDiags {
    97  						errs = append(errs, d.Description().Summary)
    98  					}
    99  					log.Printf("[TRACE] vertex %q: dynamic subgraph encountered errors: %s", dag.VertexName(v), strings.Join(errs, ","))
   100  					return
   101  				}
   102  				log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v))
   103  			} else {
   104  				log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v))
   105  			}
   106  		}
   107  		return
   108  	}
   109  
   110  	return g.AcyclicGraph.Walk(walkFn)
   111  }