opentofu/terraform/transform_output.go
James Bardin 40f09027f0 expand planned resources
While the Expander itself now handles the recursive expansion of
modules, Resources themselves still need to be expanded twice, because
the evaluation of the Resource, which entails evaluating the for_each or
count expressions, is separate from the ResourceInstance expansion.

Add a nodeExpandPlannableResource to do handle this expansion to allow
all NodePlannableResources to call EvalWriteResourceState with an
absolute address.
2020-03-25 17:03:06 -04:00

104 lines
2.5 KiB
Go

package terraform
import (
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
)
// 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
}
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 plannable 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.
for _, o := range c.Module.Outputs {
node := &NodePlannableOutput{
Addr: addrs.OutputValue{Name: o.Name},
Module: c.Path,
Config: o,
}
log.Printf("[TRACE] OutputTransformer: adding %s as %T", o.Name, node)
g.Add(node)
}
return nil
}
// DestroyOutputTransformer is a GraphTransformer that adds nodes to delete
// outputs during destroy. We need to do this to ensure that no stale outputs
// are ever left in the state.
type DestroyOutputTransformer struct {
Destroy bool
}
func (t *DestroyOutputTransformer) Transform(g *Graph) error {
// Only clean root outputs on a full destroy
if !t.Destroy {
return nil
}
for _, v := range g.Vertices() {
output, ok := v.(*NodePlannableOutput)
if !ok {
continue
}
// We only destroy root outputs
if !output.Module.Equal(addrs.RootModule) {
continue
}
// create the destroy node for this output
node := &NodeDestroyableOutput{
Addr: output.Addr.Absolute(addrs.RootModuleInstance),
Config: output.Config,
}
log.Printf("[TRACE] creating %s", node.Name())
g.Add(node)
deps, err := g.Descendents(v)
if err != nil {
return err
}
// the destroy node must depend on the eval node
deps.Add(v)
for _, d := range deps {
log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d))
g.Connect(dag.BasicEdge(node, d))
}
}
return nil
}