mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
We previously had a special case in the graph transformer for output values where it would directly create an individual output value node instead of an "expand" node as we would do for output values in nested modules. While it's true that we do always know that expanding a root module output value will always produce exactly one instance, treating this case as special creates the risk of those two codepaths diverging in other ways. Instead, we'll let the expand node also deal with root modules and minimize the special case only to how we look up any changes for the output values, since the design of plans.Changes is a bit awkward and requires us to ask the question differently for root module output values. Otherwise, the behavior will now be consistent across all output values regardless of module.
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
"github.com/hashicorp/terraform/internal/dag"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
)
|
|
|
|
// OutputTransformer is a GraphTransformer that adds all the outputs
|
|
// in the configuration to the graph.
|
|
//
|
|
// This is done for the apply graph builder even if dependent nodes
|
|
// aren't changing since there is no downside: the state will be available
|
|
// even if the dependent items aren't changing.
|
|
type OutputTransformer struct {
|
|
Config *configs.Config
|
|
Changes *plans.Changes
|
|
|
|
// If this is a planned destroy, root outputs are still in the configuration
|
|
// so we need to record that we wish to remove them
|
|
removeRootOutputs bool
|
|
|
|
// Refresh-only mode means that any failing output preconditions are
|
|
// reported as warnings rather than errors
|
|
RefreshOnly bool
|
|
}
|
|
|
|
func (t *OutputTransformer) Transform(g *Graph) error {
|
|
return t.transform(g, t.Config)
|
|
}
|
|
|
|
func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error {
|
|
// If we have no config then there can be no outputs.
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
// Transform all the children. We must do this first because
|
|
// we can reference module outputs and they must show up in the
|
|
// reference map.
|
|
for _, cc := range c.Children {
|
|
if err := t.transform(g, cc); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Add outputs to the graph, which will be dynamically expanded
|
|
// into NodeApplyableOutputs to reflect possible expansion
|
|
// through the presence of "count" or "for_each" on the modules.
|
|
|
|
var changes []*plans.OutputChangeSrc
|
|
if t.Changes != nil {
|
|
changes = t.Changes.Outputs
|
|
}
|
|
|
|
for _, o := range c.Module.Outputs {
|
|
addr := addrs.OutputValue{Name: o.Name}
|
|
|
|
var rootChange *plans.OutputChangeSrc
|
|
for _, c := range changes {
|
|
if c.Addr.Module.IsRoot() && c.Addr.OutputValue.Name == o.Name {
|
|
rootChange = c
|
|
}
|
|
}
|
|
|
|
destroy := t.removeRootOutputs
|
|
if rootChange != nil {
|
|
destroy = rootChange.Action == plans.Delete
|
|
}
|
|
|
|
// If this is a root output and we're destroying, we add the destroy
|
|
// node directly, as there is no need to expand.
|
|
|
|
var node dag.Vertex
|
|
switch {
|
|
case c.Path.IsRoot() && destroy:
|
|
node = &NodeDestroyableOutput{
|
|
Addr: addr.Absolute(addrs.RootModuleInstance),
|
|
Config: o,
|
|
}
|
|
|
|
default:
|
|
node = &nodeExpandOutput{
|
|
Addr: addr,
|
|
Module: c.Path,
|
|
Config: o,
|
|
Destroy: t.removeRootOutputs,
|
|
RefreshOnly: t.RefreshOnly,
|
|
}
|
|
}
|
|
|
|
log.Printf("[TRACE] OutputTransformer: adding %s as %T", o.Name, node)
|
|
g.Add(node)
|
|
}
|
|
|
|
return nil
|
|
}
|