diff --git a/internal/terraform/graph.go b/internal/terraform/graph.go index 559c44927718..e139f4533418 100644 --- a/internal/terraform/graph.go +++ b/internal/terraform/graph.go @@ -67,21 +67,34 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { } }() - // vertexCtx is the context that we use when evaluating. This - // is normally the context of our graph but can be overridden - // with a GraphNodeModuleInstance impl. - vertexCtx := ctx - if pn, ok := v.(GraphNodeModuleInstance); ok { - vertexCtx = walker.EnterPath(pn.Path()) - defer walker.ExitPath(pn.Path()) - } - if g.checkAndApplyOverrides(ctx.Overrides(), v) { // We can skip whole vertices if they are in a module that has been // overridden. + log.Printf("[TRACE] vertex %q: overridden by a test double, so skipping", dag.VertexName(v)) return } + // vertexCtx is the context that we use when evaluating. This + // is normally the global context but can be overridden + // with a GraphNodeModuleInstance or GraphNodePartialExpandedModule + // impl. (The two interfaces are intentionally mutually-exclusive by + // both having the same method name but with different signatures, + // since a node can't belong to two different contexts at once.) + vertexCtx := ctx + if pn, ok := v.(GraphNodeModuleInstance); ok { + moduleAddr := pn.Path() // An addrs.ModuleInstance + log.Printf("[TRACE] vertex %q: belongs to %s", dag.VertexName(v), moduleAddr) + vertexCtx = walker.EnterPath(moduleAddr) + defer walker.ExitPath(pn.Path()) + } else if pn, ok := v.(GraphNodePartialExpandedModule); ok { + moduleAddr := pn.Path() // An addrs.PartialExpandedModule + log.Printf("[TRACE] vertex %q: belongs to all of %s", dag.VertexName(v), moduleAddr) + vertexCtx = walker.EnterPartialExpandedPath(pn.Path()) + defer walker.ExitPartialExpandedPath(pn.Path()) + } else { + log.Printf("[TRACE] vertex %q: does not belong to any module instance", dag.VertexName(v)) + } + // If the node is exec-able, then execute it. if ev, ok := v.(GraphNodeExecutable); ok { diags = diags.Append(walker.Execute(vertexCtx, ev)) diff --git a/internal/terraform/graph_interface_subgraph.go b/internal/terraform/graph_interface_subgraph.go index 2e013aeca0fa..f70fdeb84bd8 100644 --- a/internal/terraform/graph_interface_subgraph.go +++ b/internal/terraform/graph_interface_subgraph.go @@ -18,3 +18,21 @@ type GraphNodeModuleInstance interface { type GraphNodeModulePath interface { ModulePath() addrs.Module } + +// GraphNodePartialExpandedModule says that a node represents an unbounded +// set of objects within an unbounded set of module instances that happen +// to share a known address prefix. +// +// Nodes of this type typically produce placeholder data to support partial +// evaluation despite the full analysis of a module being deferred to a future +// plan when more information will be available. They might also perform +// checks and raise errors when something can be proven to be definitely +// invalid regardless of what the final set of module instances turns out to +// be. +// +// Node types implementing this interface cannot also implement +// [GraphNodeModuleInstance], because it is not possible to evaluate a +// node in two different contexts at once. +type GraphNodePartialExpandedModule interface { + Path() addrs.PartialExpandedModule +} diff --git a/internal/terraform/node_local.go b/internal/terraform/node_local.go index defd5fc10a21..067878e534a6 100644 --- a/internal/terraform/node_local.go +++ b/internal/terraform/node_local.go @@ -201,4 +201,8 @@ type nodeLocalInPartialModule struct { Config *configs.Local } +func (n *nodeLocalInPartialModule) Path() addrs.PartialExpandedModule { + return n.Addr.Module +} + // TODO: Implement nodeLocalUnexpandedPlaceholder.Execute diff --git a/internal/terraform/node_module_variable.go b/internal/terraform/node_module_variable.go index 93c23a562a3b..11a053e77224 100644 --- a/internal/terraform/node_module_variable.go +++ b/internal/terraform/node_module_variable.go @@ -323,4 +323,8 @@ type nodeModuleVariableInPartialModule struct { DestroyApply bool } +func (n *nodeModuleVariableInPartialModule) Path() addrs.PartialExpandedModule { + return n.Addr.Module +} + // TODO: Implement nodeModuleVariableInPartialModule.Execute diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index bfa53d1fa6cb..3be1fceeee9e 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -464,6 +464,10 @@ type nodeOutputInPartialModule struct { RefreshOnly bool } +func (n *nodeOutputInPartialModule) Path() addrs.PartialExpandedModule { + return n.Addr.Module +} + // TODO: Implement nodeOutputInPartialModule.Execute // NodeDestroyableOutput represents an output that is "destroyable":