github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_root.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tofu
     7  
     8  import (
     9  	"github.com/opentofu/opentofu/internal/dag"
    10  )
    11  
    12  const rootNodeName = "root"
    13  
    14  // RootTransformer is a GraphTransformer that adds a root to the graph.
    15  type RootTransformer struct{}
    16  
    17  func (t *RootTransformer) Transform(g *Graph) error {
    18  	addRootNodeToGraph(g)
    19  	return nil
    20  }
    21  
    22  // addRootNodeToGraph modifies the given graph in-place so that it has a root
    23  // node if it didn't already have one and so that any other node which doesn't
    24  // already depend on something will depend on that root node.
    25  //
    26  // After this function returns, the graph will have only one node that doesn't
    27  // depend on any other nodes.
    28  func addRootNodeToGraph(g *Graph) {
    29  	// We always add the root node. This is a singleton so if it's already
    30  	// in the graph this will do nothing and just retain the existing root node.
    31  	//
    32  	// Note that rootNode is intentionally added by value and not by pointer
    33  	// so that all root nodes will be equal to one another and therefore
    34  	// coalesce when two valid graphs get merged together into a single graph.
    35  	g.Add(rootNode)
    36  
    37  	// Everything that doesn't already depend on at least one other node will
    38  	// depend on the root node, except the root node itself.
    39  	for _, v := range g.Vertices() {
    40  		if v == dag.Vertex(rootNode) {
    41  			continue
    42  		}
    43  
    44  		if g.UpEdges(v).Len() == 0 {
    45  			g.Connect(dag.BasicEdge(rootNode, v))
    46  		}
    47  	}
    48  }
    49  
    50  type graphNodeRoot struct{}
    51  
    52  // rootNode is the singleton value representing all root graph nodes.
    53  //
    54  // The root node for all graphs should be this value directly, and in particular
    55  // _not_ a pointer to this value. Using the value directly here means that
    56  // multiple root nodes will always coalesce together when subsuming one graph
    57  // into another.
    58  var rootNode graphNodeRoot
    59  
    60  func (n graphNodeRoot) Name() string {
    61  	return rootNodeName
    62  }
    63  
    64  // CloseRootModuleTransformer is a GraphTransformer that adds a root to the graph.
    65  type CloseRootModuleTransformer struct{}
    66  
    67  func (t *CloseRootModuleTransformer) Transform(g *Graph) error {
    68  	// close the root module
    69  	closeRoot := &nodeCloseModule{}
    70  	g.Add(closeRoot)
    71  
    72  	// since this is closing the root module, make it depend on everything in
    73  	// the root module.
    74  	for _, v := range g.Vertices() {
    75  		if v == closeRoot {
    76  			continue
    77  		}
    78  
    79  		// since this is closing the root module,  and must be last, we can
    80  		// connect to anything that doesn't have any up edges.
    81  		if g.UpEdges(v).Len() == 0 {
    82  			g.Connect(dag.BasicEdge(closeRoot, v))
    83  		}
    84  	}
    85  
    86  	return nil
    87  }