mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-07 14:44:11 -06:00
a103c65140
Evaluate precondition and postcondition blocks in refresh-only mode, but report any failures as warnings instead of errors. This ensures that any deviation from the contract defined by condition blocks is reported as early as possible, without preventing the completion of a state refresh operation. Prior to this commit, Terraform evaluated output preconditions and data source pre/postconditions as normal in refresh-only mode, while managed resource pre/postconditions were not evaluated at all. This omission could lead to confusing partial condition errors, or failure to detect undesired changes which would otherwise cause resources to become invalid. Reporting the failures as errors also meant that changes retrieved during refresh could cause the refresh operation to fail. This is also undesirable, as the primary purpose of the operation is to update local state. Precondition/postcondition checks are still valuable here, but should be informative rather than blocking.
110 lines
2.8 KiB
Go
110 lines
2.8 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
|
|
Destroy 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.Destroy
|
|
if rootChange != nil {
|
|
destroy = rootChange.Action == plans.Delete
|
|
}
|
|
|
|
// If this is a root output, we add the apply or destroy node directly,
|
|
// as the root modules does not expand.
|
|
|
|
var node dag.Vertex
|
|
switch {
|
|
case c.Path.IsRoot() && destroy:
|
|
node = &NodeDestroyableOutput{
|
|
Addr: addr.Absolute(addrs.RootModuleInstance),
|
|
Config: o,
|
|
}
|
|
|
|
case c.Path.IsRoot():
|
|
node = &NodeApplyableOutput{
|
|
Addr: addr.Absolute(addrs.RootModuleInstance),
|
|
Config: o,
|
|
Change: rootChange,
|
|
RefreshOnly: t.RefreshOnly,
|
|
}
|
|
|
|
default:
|
|
node = &nodeExpandOutput{
|
|
Addr: addr,
|
|
Module: c.Path,
|
|
Config: o,
|
|
Changes: changes,
|
|
Destroy: t.Destroy,
|
|
RefreshOnly: t.RefreshOnly,
|
|
}
|
|
}
|
|
|
|
log.Printf("[TRACE] OutputTransformer: adding %s as %T", o.Name, node)
|
|
g.Add(node)
|
|
}
|
|
|
|
return nil
|
|
}
|