github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/terraform/graph.go (about)

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