eval variables with unknown expansion data
While we don't have any expansion info during validation, we can try to evaluate variable expressions to catch some basic errors. Do this by creating module instance RepetitionData with unknown values. This unfortunately will still miss the incorrect usage of count/each values, but that would require the module call's each mode, which is not available at this time.
This commit is contained in:
parent
c59ecac870
commit
d060a3d0e8
|
@ -1435,7 +1435,7 @@ resource "aws_instance" "foo" {
|
||||||
module "nested" {
|
module "nested" {
|
||||||
count = 2
|
count = 2
|
||||||
source = "./nested"
|
source = "./nested"
|
||||||
input = 2
|
input = count.index
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
"mod/nested/main.tf": `
|
"mod/nested/main.tf": `
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
|
"github.com/hashicorp/terraform/instances"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
|
@ -42,12 +43,12 @@ type EvalModuleCallArgument struct {
|
||||||
Expr hcl.Expression
|
Expr hcl.Expression
|
||||||
ModuleInstance addrs.ModuleInstance
|
ModuleInstance addrs.ModuleInstance
|
||||||
|
|
||||||
// If this flag is set, any diagnostics are discarded and this operation
|
|
||||||
// will always succeed, though may produce an unknown value in the
|
|
||||||
// event of an error.
|
|
||||||
IgnoreDiagnostics bool
|
|
||||||
|
|
||||||
Values map[string]cty.Value
|
Values map[string]cty.Value
|
||||||
|
|
||||||
|
// validateOnly indicates that this evaluation is only for config
|
||||||
|
// validation, and we will not have any expansion module instance
|
||||||
|
// repetition data.
|
||||||
|
validateOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
|
func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
|
@ -69,9 +70,24 @@ func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the repetition data for this module instance,
|
var moduleInstanceRepetitionData instances.RepetitionData
|
||||||
// so we can create the appropriate scope for evaluating our expression
|
|
||||||
moduleInstanceRepetitionData := ctx.InstanceExpander().GetModuleInstanceRepetitionData(n.ModuleInstance)
|
switch {
|
||||||
|
case n.validateOnly:
|
||||||
|
// the instance expander does not track unknown expansion values, so we
|
||||||
|
// have to assume all RepetitionData is unknown.
|
||||||
|
moduleInstanceRepetitionData = instances.RepetitionData{
|
||||||
|
CountIndex: cty.UnknownVal(cty.Number),
|
||||||
|
EachKey: cty.UnknownVal(cty.String),
|
||||||
|
EachValue: cty.DynamicVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Get the repetition data for this module instance,
|
||||||
|
// so we can create the appropriate scope for evaluating our expression
|
||||||
|
moduleInstanceRepetitionData = ctx.InstanceExpander().GetModuleInstanceRepetitionData(n.ModuleInstance)
|
||||||
|
}
|
||||||
|
|
||||||
scope := ctx.EvaluationScope(nil, moduleInstanceRepetitionData)
|
scope := ctx.EvaluationScope(nil, moduleInstanceRepetitionData)
|
||||||
val, diags := scope.EvalExpr(expr, cty.DynamicPseudoType)
|
val, diags := scope.EvalExpr(expr, cty.DynamicPseudoType)
|
||||||
|
|
||||||
|
@ -96,9 +112,6 @@ func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Values[name] = val
|
n.Values[name] = val
|
||||||
if n.IgnoreDiagnostics {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, diags.ErrWithWarnings()
|
return nil, diags.ErrWithWarnings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,15 +129,10 @@ type evalVariableValidations struct {
|
||||||
// This will be nil for root module variables, because their values come
|
// This will be nil for root module variables, because their values come
|
||||||
// from outside the configuration.
|
// from outside the configuration.
|
||||||
Expr hcl.Expression
|
Expr hcl.Expression
|
||||||
|
|
||||||
// If this flag is set, this node becomes a no-op.
|
|
||||||
// This is here for consistency with EvalModuleCallArgument so that it
|
|
||||||
// can be populated with the same value, where needed.
|
|
||||||
IgnoreDiagnostics bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *evalVariableValidations) Eval(ctx EvalContext) (interface{}, error) {
|
func (n *evalVariableValidations) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
if n.Config == nil || n.IgnoreDiagnostics || len(n.Config.Validations) == 0 {
|
if n.Config == nil || len(n.Config.Validations) == 0 {
|
||||||
log.Printf("[TRACE] evalVariableValidations: not active for %s, so skipping", n.Addr)
|
log.Printf("[TRACE] evalVariableValidations: not active for %s, so skipping", n.Addr)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ import (
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConcreteVariableNodeFunc func(n *nodeExpandModuleVariable) dag.Vertex
|
|
||||||
|
|
||||||
// nodeExpandModuleVariable is the placeholder for an variable that has not yet had
|
// nodeExpandModuleVariable is the placeholder for an variable that has not yet had
|
||||||
// its module path expanded.
|
// its module path expanded.
|
||||||
type nodeExpandModuleVariable struct {
|
type nodeExpandModuleVariable struct {
|
||||||
|
@ -176,15 +174,25 @@ func (n *nodeModuleVariable) EvalTree() EvalNode {
|
||||||
Nodes: []EvalNode{
|
Nodes: []EvalNode{
|
||||||
&EvalOpFilter{
|
&EvalOpFilter{
|
||||||
Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
|
Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
|
||||||
walkDestroy, walkValidate},
|
walkDestroy},
|
||||||
Node: &EvalModuleCallArgument{
|
Node: &EvalModuleCallArgument{
|
||||||
Addr: n.Addr.Variable,
|
Addr: n.Addr.Variable,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Expr: n.Expr,
|
Expr: n.Expr,
|
||||||
ModuleInstance: n.ModuleInstance,
|
ModuleInstance: n.ModuleInstance,
|
||||||
Values: vals,
|
Values: vals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
IgnoreDiagnostics: false,
|
&EvalOpFilter{
|
||||||
|
Ops: []walkOperation{walkValidate},
|
||||||
|
Node: &EvalModuleCallArgument{
|
||||||
|
Addr: n.Addr.Variable,
|
||||||
|
Config: n.Config,
|
||||||
|
Expr: n.Expr,
|
||||||
|
ModuleInstance: n.ModuleInstance,
|
||||||
|
Values: vals,
|
||||||
|
validateOnly: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -197,8 +205,6 @@ func (n *nodeModuleVariable) EvalTree() EvalNode {
|
||||||
Addr: n.Addr,
|
Addr: n.Addr,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Expr: n.Expr,
|
Expr: n.Expr,
|
||||||
|
|
||||||
IgnoreDiagnostics: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,6 @@ func (n *NodeRootVariable) EvalTree() EvalNode {
|
||||||
Addr: addrs.RootModuleInstance.InputVariable(n.Addr.Name),
|
Addr: addrs.RootModuleInstance.InputVariable(n.Addr.Name),
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Expr: nil, // not set for root module variables
|
Expr: nil, // not set for root module variables
|
||||||
|
|
||||||
IgnoreDiagnostics: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,6 @@ func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs
|
||||||
Config: v,
|
Config: v,
|
||||||
Expr: expr,
|
Expr: expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Add(node)
|
g.Add(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue