mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
We use a non-pointer value for this particular node, which means that there can never be two root nodes in the same graph: the graph implementation will just coalesce them together when a second one is added. Our resource expansion code is relying on that coalescing so that it can subsume together multiple graphs for different modules instances into a single mega-graph with all instances across all module instances, with any root nodes coalescing together to produce a single root. This also updates one of the context tests that exercises resource expansion so that it will generate multiple resource instance nodes per module and thus potentially have multiple roots to coalesce together. However, we aren't currently explicitly validating the return values from DynamicExpand and so this test doesn't actually fail if the coalescing doesn't happen. We may choose to validate the DynamicExpand result in a later commit in order to make it more obvious if future modifications fail to uphold this invariant.
76 lines
2.0 KiB
Go
76 lines
2.0 KiB
Go
package terraform
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform/internal/dag"
|
|
)
|
|
|
|
const rootNodeName = "root"
|
|
|
|
// RootTransformer is a GraphTransformer that adds a root to the graph.
|
|
type RootTransformer struct{}
|
|
|
|
func (t *RootTransformer) Transform(g *Graph) error {
|
|
// If we already have a good root, we're done
|
|
if _, err := g.Root(); err == nil {
|
|
return nil
|
|
}
|
|
|
|
// We intentionally add a graphNodeRoot value -- rather than a pointer to
|
|
// one -- so that all root nodes will coalesce together if two graphs
|
|
// are merged. Each distinct node value can only be in a graph once,
|
|
// so adding another graphNodeRoot value to the same graph later will
|
|
// be a no-op and all of the edges from root nodes will coalesce together
|
|
// under Graph.Subsume.
|
|
//
|
|
// It's important to retain this coalescing guarantee under future
|
|
// maintenence.
|
|
var root graphNodeRoot
|
|
g.Add(root)
|
|
|
|
// We initially make the root node depend on every node except itself.
|
|
// If the caller subsequently runs transitive reduction on the graph then
|
|
// it's typical for some of these edges to then be removed.
|
|
for _, v := range g.Vertices() {
|
|
if v == root {
|
|
continue
|
|
}
|
|
|
|
if g.UpEdges(v).Len() == 0 {
|
|
g.Connect(dag.BasicEdge(root, v))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type graphNodeRoot struct{}
|
|
|
|
func (n graphNodeRoot) Name() string {
|
|
return rootNodeName
|
|
}
|
|
|
|
// CloseRootModuleTransformer is a GraphTransformer that adds a root to the graph.
|
|
type CloseRootModuleTransformer struct{}
|
|
|
|
func (t *CloseRootModuleTransformer) Transform(g *Graph) error {
|
|
// close the root module
|
|
closeRoot := &nodeCloseModule{}
|
|
g.Add(closeRoot)
|
|
|
|
// since this is closing the root module, make it depend on everything in
|
|
// the root module.
|
|
for _, v := range g.Vertices() {
|
|
if v == closeRoot {
|
|
continue
|
|
}
|
|
|
|
// since this is closing the root module, and must be last, we can
|
|
// connect to anything that doesn't have any up edges.
|
|
if g.UpEdges(v).Len() == 0 {
|
|
g.Connect(dag.BasicEdge(closeRoot, v))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|