opentofu/terraform/node_module_variable.go
James Bardin a2d2ce35dc remove "prepare state" from expanders
That name tag was left in only to reduce the diff when during
implementation. Fix the naming now for these nodes so it is correct, and
prevent any possible name collision between types.
2020-05-12 10:28:33 -04:00

223 lines
6.2 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)
_ RemovableIfNotTargeted = (*nodeExpandModuleVariable)(nil)
)
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 {
// FIXME: References for module variables probably need to be thought out a bit more
// Otherwise, we can reference the output via the address itself, or the
// module call
_, call := n.Module.Call()
return []addrs.Referenceable{n.Addr, call}
}
// RemovableIfNotTargeted
func (n *nodeExpandModuleVariable) RemoveIfNotTargeted() bool {
return true
}
// GraphNodeTargetDownstream
func (n *nodeExpandModuleVariable) TargetDownstream(targetedDeps, untargetedDeps dag.Set) bool {
return true
}
// 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)
_ RemovableIfNotTargeted = (*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.Parent().Module()
}
// RemovableIfNotTargeted
func (n *nodeModuleVariable) RemoveIfNotTargeted() bool {
// We need to add this so that this node will be removed if
// it isn't targeted or a dependency of a target.
return true
}
// 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},
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",
},
}
}