diff --git a/terraform/eval_context_mock.go b/terraform/eval_context_mock.go index 2d52243d76..49dbc688d0 100644 --- a/terraform/eval_context_mock.go +++ b/terraform/eval_context_mock.go @@ -3,15 +3,16 @@ package terraform import ( "sync" - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/lang" - "github.com/hashicorp/hcl2/hcl" - "github.com/hashicorp/terraform/config/configschema" - "github.com/hashicorp/terraform/tfdiags" + "github.com/hashicorp/hcl2/hcldec" "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/convert" + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/configschema" + "github.com/hashicorp/terraform/lang" + "github.com/hashicorp/terraform/tfdiags" ) // MockEvalContext is a mock version of EvalContext that can be used @@ -75,21 +76,32 @@ type MockEvalContext struct { CloseProvisionerName string CloseProvisionerProvisioner ResourceProvisioner - EvaluateBlockCalled bool - EvaluateBlockBody hcl.Body - EvaluateBlockSchema *configschema.Block - EvaluateBlockSelf addrs.Referenceable - EvaluateBlockKey addrs.InstanceKey + EvaluateBlockCalled bool + EvaluateBlockBody hcl.Body + EvaluateBlockSchema *configschema.Block + EvaluateBlockSelf addrs.Referenceable + EvaluateBlockKey addrs.InstanceKey + EvaluateBlockResultFunc func( + body hcl.Body, + schema *configschema.Block, + self addrs.Referenceable, + key addrs.InstanceKey, + ) (cty.Value, hcl.Body, tfdiags.Diagnostics) // overrides the other values below, if set EvaluateBlockResult cty.Value EvaluateBlockExpandedBody hcl.Body EvaluateBlockDiags tfdiags.Diagnostics - EvaluateExprCalled bool - EvaluateExprExpr hcl.Expression - EvaluateExprWantType cty.Type - EvaluateExprSelf addrs.Referenceable - EvaluateExprResult cty.Value - EvaluateExprDiags tfdiags.Diagnostics + EvaluateExprCalled bool + EvaluateExprExpr hcl.Expression + EvaluateExprWantType cty.Type + EvaluateExprSelf addrs.Referenceable + EvaluateExprResultFunc func( + expr hcl.Expression, + wantType cty.Type, + self addrs.Referenceable, + ) (cty.Value, tfdiags.Diagnostics) // overrides the other values below, if set + EvaluateExprResult cty.Value + EvaluateExprDiags tfdiags.Diagnostics EvaluationScopeCalled bool EvaluationScopeSelf addrs.Referenceable @@ -222,6 +234,9 @@ func (c *MockEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Bloc c.EvaluateBlockSchema = schema c.EvaluateBlockSelf = self c.EvaluateBlockKey = key + if c.EvaluateBlockResultFunc != nil { + return c.EvaluateBlockResultFunc(body, schema, self, key) + } return c.EvaluateBlockResult, c.EvaluateBlockExpandedBody, c.EvaluateBlockDiags } @@ -230,9 +245,65 @@ func (c *MockEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, s c.EvaluateExprExpr = expr c.EvaluateExprWantType = wantType c.EvaluateExprSelf = self + if c.EvaluateExprResultFunc != nil { + return c.EvaluateExprResultFunc(expr, wantType, self) + } return c.EvaluateExprResult, c.EvaluateExprDiags } +// installSimpleEval is a helper to install a simple mock implementation of +// both EvaluateBlock and EvaluateExpr into the receiver. +// +// These default implementations will either evaluate the given input against +// the scope in field EvaluationScopeScope or, if it is nil, with no eval +// context at all so that only constant values may be used. +// +// This function overwrites any existing functions installed in fields +// EvaluateBlockResultFunc and EvaluateExprResultFunc. +func (c *MockEvalContext) installSimpleEval() { + c.EvaluateBlockResultFunc = func(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, key addrs.InstanceKey) (cty.Value, hcl.Body, tfdiags.Diagnostics) { + if scope := c.EvaluationScopeScope; scope != nil { + // Fully-functional codepath. + var diags tfdiags.Diagnostics + body, diags = scope.ExpandBlock(body, schema) + if diags.HasErrors() { + return cty.DynamicVal, body, diags + } + val, evalDiags := c.EvaluationScopeScope.EvalBlock(body, schema) + diags = diags.Append(evalDiags) + if evalDiags.HasErrors() { + return cty.DynamicVal, body, diags + } + return val, body, diags + } + + // Fallback codepath supporting constant values only. + val, hclDiags := hcldec.Decode(body, schema.DecoderSpec(), nil) + return val, body, tfdiags.Diagnostics(nil).Append(hclDiags) + } + c.EvaluateExprResultFunc = func(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { + if scope := c.EvaluationScopeScope; scope != nil { + // Fully-functional codepath. + return scope.EvalExpr(expr, wantType) + } + + // Fallback codepath supporting constant values only. + var diags tfdiags.Diagnostics + val, hclDiags := expr.Value(nil) + diags = diags.Append(hclDiags) + if hclDiags.HasErrors() { + return cty.DynamicVal, diags + } + var err error + val, err = convert.Convert(val, wantType) + if err != nil { + diags = diags.Append(err) + return cty.DynamicVal, diags + } + return val, diags + } +} + func (c *MockEvalContext) EvaluationScope(self addrs.Referenceable, key addrs.InstanceKey) *lang.Scope { c.EvaluationScopeCalled = true c.EvaluationScopeSelf = self