mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
d8e9964363
Include the import walk in the list of operations for which we create an EvalModuleCallArgument node. This causes module call arguments to be evaluated even if the module variables have defaults, ensuring that invalid default values (such as the common "{}" for variables thought of as maps) do not cause failures specific to import. This fixes a bug where a child module evaluates an input variable in its locals block, assuming that it is a nested object structure. The bug report includes a default value of "{}", which is overridden by a root variable value. Without the eval node added in this commit, the default value is used and the local evaluation errors.
203 lines
5.6 KiB
Go
203 lines
5.6 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/dag"
|
|
"github.com/hashicorp/terraform/lang"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// nodeExpandModuleVariable is the placeholder for an variable that has not yet had
|
|
// its module path expanded.
|
|
type nodeExpandModuleVariable struct {
|
|
Addr addrs.InputVariable
|
|
Module addrs.Module
|
|
Config *configs.Variable
|
|
Expr hcl.Expression
|
|
}
|
|
|
|
var (
|
|
_ GraphNodeDynamicExpandable = (*nodeExpandModuleVariable)(nil)
|
|
_ GraphNodeReferenceOutside = (*nodeExpandModuleVariable)(nil)
|
|
_ GraphNodeReferenceable = (*nodeExpandModuleVariable)(nil)
|
|
_ GraphNodeReferencer = (*nodeExpandModuleVariable)(nil)
|
|
_ graphNodeTemporaryValue = (*nodeExpandModuleVariable)(nil)
|
|
_ graphNodeExpandsInstances = (*nodeExpandModuleVariable)(nil)
|
|
)
|
|
|
|
func (n *nodeExpandModuleVariable) expandsInstances() {}
|
|
|
|
func (n *nodeExpandModuleVariable) temporaryValue() bool {
|
|
return true
|
|
}
|
|
|
|
func (n *nodeExpandModuleVariable) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
|
var g Graph
|
|
expander := ctx.InstanceExpander()
|
|
for _, module := range expander.ExpandModule(n.Module) {
|
|
o := &nodeModuleVariable{
|
|
Addr: n.Addr.Absolute(module),
|
|
Config: n.Config,
|
|
Expr: n.Expr,
|
|
ModuleInstance: module,
|
|
}
|
|
g.Add(o)
|
|
}
|
|
return &g, nil
|
|
}
|
|
|
|
func (n *nodeExpandModuleVariable) Name() string {
|
|
return fmt.Sprintf("%s.%s (expand)", n.Module, n.Addr.String())
|
|
}
|
|
|
|
// GraphNodeModulePath
|
|
func (n *nodeExpandModuleVariable) ModulePath() addrs.Module {
|
|
return n.Module
|
|
}
|
|
|
|
// GraphNodeReferencer
|
|
func (n *nodeExpandModuleVariable) References() []*addrs.Reference {
|
|
|
|
// If we have no value expression, we cannot depend on anything.
|
|
if n.Expr == nil {
|
|
return nil
|
|
}
|
|
|
|
// Variables in the root don't depend on anything, because their values
|
|
// are gathered prior to the graph walk and recorded in the context.
|
|
if len(n.Module) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Otherwise, we depend on anything referenced by our value expression.
|
|
// We ignore diagnostics here under the assumption that we'll re-eval
|
|
// all these things later and catch them then; for our purposes here,
|
|
// we only care about valid references.
|
|
//
|
|
// Due to our GraphNodeReferenceOutside implementation, the addresses
|
|
// returned by this function are interpreted in the _parent_ module from
|
|
// where our associated variable was declared, which is correct because
|
|
// our value expression is assigned within a "module" block in the parent
|
|
// module.
|
|
refs, _ := lang.ReferencesInExpr(n.Expr)
|
|
return refs
|
|
}
|
|
|
|
// GraphNodeReferenceOutside implementation
|
|
func (n *nodeExpandModuleVariable) ReferenceOutside() (selfPath, referencePath addrs.Module) {
|
|
return n.Module, n.Module.Parent()
|
|
}
|
|
|
|
// GraphNodeReferenceable
|
|
func (n *nodeExpandModuleVariable) ReferenceableAddrs() []addrs.Referenceable {
|
|
return []addrs.Referenceable{n.Addr}
|
|
}
|
|
|
|
// nodeModuleVariable represents a module variable input during
|
|
// the apply step.
|
|
type nodeModuleVariable struct {
|
|
Addr addrs.AbsInputVariableInstance
|
|
Config *configs.Variable // Config is the var in the config
|
|
Expr hcl.Expression // Expr is the value expression given in the call
|
|
// ModuleInstance in order to create the appropriate context for evaluating
|
|
// ModuleCallArguments, ex. so count.index and each.key can resolve
|
|
ModuleInstance addrs.ModuleInstance
|
|
}
|
|
|
|
// Ensure that we are implementing all of the interfaces we think we are
|
|
// implementing.
|
|
var (
|
|
_ GraphNodeModuleInstance = (*nodeModuleVariable)(nil)
|
|
_ GraphNodeEvalable = (*nodeModuleVariable)(nil)
|
|
_ graphNodeTemporaryValue = (*nodeModuleVariable)(nil)
|
|
_ dag.GraphNodeDotter = (*nodeModuleVariable)(nil)
|
|
)
|
|
|
|
func (n *nodeModuleVariable) temporaryValue() bool {
|
|
return true
|
|
}
|
|
|
|
func (n *nodeModuleVariable) Name() string {
|
|
return n.Addr.String()
|
|
}
|
|
|
|
// GraphNodeModuleInstance
|
|
func (n *nodeModuleVariable) Path() addrs.ModuleInstance {
|
|
// We execute in the parent scope (above our own module) because
|
|
// expressions in our value are resolved in that context.
|
|
return n.Addr.Module.Parent()
|
|
}
|
|
|
|
// GraphNodeModulePath
|
|
func (n *nodeModuleVariable) ModulePath() addrs.Module {
|
|
return n.Addr.Module.Module()
|
|
}
|
|
|
|
// GraphNodeEvalable
|
|
func (n *nodeModuleVariable) EvalTree() EvalNode {
|
|
// If we have no value, do nothing
|
|
if n.Expr == nil {
|
|
return &EvalNoop{}
|
|
}
|
|
|
|
// Otherwise, interpolate the value of this variable and set it
|
|
// within the variables mapping.
|
|
vals := make(map[string]cty.Value)
|
|
|
|
_, call := n.Addr.Module.CallInstance()
|
|
|
|
return &EvalSequence{
|
|
Nodes: []EvalNode{
|
|
&EvalOpFilter{
|
|
Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
|
|
walkDestroy, walkImport},
|
|
Node: &EvalModuleCallArgument{
|
|
Addr: n.Addr.Variable,
|
|
Config: n.Config,
|
|
Expr: n.Expr,
|
|
ModuleInstance: n.ModuleInstance,
|
|
Values: vals,
|
|
},
|
|
},
|
|
|
|
&EvalOpFilter{
|
|
Ops: []walkOperation{walkValidate},
|
|
Node: &EvalModuleCallArgument{
|
|
Addr: n.Addr.Variable,
|
|
Config: n.Config,
|
|
Expr: n.Expr,
|
|
ModuleInstance: n.ModuleInstance,
|
|
Values: vals,
|
|
validateOnly: true,
|
|
},
|
|
},
|
|
|
|
&EvalSetModuleCallArguments{
|
|
Module: call,
|
|
Values: vals,
|
|
},
|
|
|
|
&evalVariableValidations{
|
|
Addr: n.Addr,
|
|
Config: n.Config,
|
|
Expr: n.Expr,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// dag.GraphNodeDotter impl.
|
|
func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
|
|
return &dag.DotNode{
|
|
Name: name,
|
|
Attrs: map[string]string{
|
|
"label": n.Name(),
|
|
"shape": "note",
|
|
},
|
|
}
|
|
}
|