github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/graph.go (about)

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