mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
3785619f93
The "checks" package is an expansion what we previously called plans.Conditions to accommodate a new requirement that we be able to track which checks we're expecting to run even if we don't actually get around to running them, which will be helpful when we start using checks as part of our module testing story because test reporting tools appreciate there being a relatively consistent set of test cases from one run to the next. So far this should be essentially a no-op change from an external functionality standpoint, aside from some minor adjustments to how we report some of the error and warning cases from condition evaluation in light of the fact that the "checks" package can now track errors as a different outcome than a failure of a valid check. As is often the case with anything which changes what we track in the EvalContext and persist between plan and apply, Terraform Core is pretty brittle and so this had knock-on effects elsewhere too. Again, the goal is for these changes to not create any material externally-visible difference, and just to accommodate the new assumption that there will always be a "checks" object available for tracking during a graph walk.
402 lines
12 KiB
Go
402 lines
12 KiB
Go
package terraform
|
|
|
|
import (
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hcldec"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/checks"
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/instances"
|
|
"github.com/hashicorp/terraform/internal/lang"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/provisioners"
|
|
"github.com/hashicorp/terraform/internal/refactoring"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
)
|
|
|
|
// MockEvalContext is a mock version of EvalContext that can be used
|
|
// for tests.
|
|
type MockEvalContext struct {
|
|
StoppedCalled bool
|
|
StoppedValue <-chan struct{}
|
|
|
|
HookCalled bool
|
|
HookHook Hook
|
|
HookError error
|
|
|
|
InputCalled bool
|
|
InputInput UIInput
|
|
|
|
InitProviderCalled bool
|
|
InitProviderType string
|
|
InitProviderAddr addrs.AbsProviderConfig
|
|
InitProviderProvider providers.Interface
|
|
InitProviderError error
|
|
|
|
ProviderCalled bool
|
|
ProviderAddr addrs.AbsProviderConfig
|
|
ProviderProvider providers.Interface
|
|
|
|
ProviderSchemaCalled bool
|
|
ProviderSchemaAddr addrs.AbsProviderConfig
|
|
ProviderSchemaSchema *ProviderSchema
|
|
ProviderSchemaError error
|
|
|
|
CloseProviderCalled bool
|
|
CloseProviderAddr addrs.AbsProviderConfig
|
|
CloseProviderProvider providers.Interface
|
|
|
|
ProviderInputCalled bool
|
|
ProviderInputAddr addrs.AbsProviderConfig
|
|
ProviderInputValues map[string]cty.Value
|
|
|
|
SetProviderInputCalled bool
|
|
SetProviderInputAddr addrs.AbsProviderConfig
|
|
SetProviderInputValues map[string]cty.Value
|
|
|
|
ConfigureProviderFn func(
|
|
addr addrs.AbsProviderConfig,
|
|
cfg cty.Value) tfdiags.Diagnostics // overrides the other values below, if set
|
|
ConfigureProviderCalled bool
|
|
ConfigureProviderAddr addrs.AbsProviderConfig
|
|
ConfigureProviderConfig cty.Value
|
|
ConfigureProviderDiags tfdiags.Diagnostics
|
|
|
|
ProvisionerCalled bool
|
|
ProvisionerName string
|
|
ProvisionerProvisioner provisioners.Interface
|
|
|
|
ProvisionerSchemaCalled bool
|
|
ProvisionerSchemaName string
|
|
ProvisionerSchemaSchema *configschema.Block
|
|
ProvisionerSchemaError error
|
|
|
|
CloseProvisionersCalled bool
|
|
|
|
EvaluateBlockCalled bool
|
|
EvaluateBlockBody hcl.Body
|
|
EvaluateBlockSchema *configschema.Block
|
|
EvaluateBlockSelf addrs.Referenceable
|
|
EvaluateBlockKeyData InstanceKeyEvalData
|
|
EvaluateBlockResultFunc func(
|
|
body hcl.Body,
|
|
schema *configschema.Block,
|
|
self addrs.Referenceable,
|
|
keyData InstanceKeyEvalData,
|
|
) (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
|
|
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
|
|
EvaluationScopeKeyData InstanceKeyEvalData
|
|
EvaluationScopeScope *lang.Scope
|
|
|
|
PathCalled bool
|
|
PathPath addrs.ModuleInstance
|
|
|
|
SetRootModuleArgumentCalled bool
|
|
SetRootModuleArgumentAddr addrs.InputVariable
|
|
SetRootModuleArgumentValue cty.Value
|
|
SetRootModuleArgumentFunc func(addr addrs.InputVariable, v cty.Value)
|
|
|
|
SetModuleCallArgumentCalled bool
|
|
SetModuleCallArgumentModuleCall addrs.ModuleCallInstance
|
|
SetModuleCallArgumentVariable addrs.InputVariable
|
|
SetModuleCallArgumentValue cty.Value
|
|
SetModuleCallArgumentFunc func(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value)
|
|
|
|
GetVariableValueCalled bool
|
|
GetVariableValueAddr addrs.AbsInputVariableInstance
|
|
GetVariableValueValue cty.Value
|
|
GetVariableValueFunc func(addr addrs.AbsInputVariableInstance) cty.Value // supersedes GetVariableValueValue
|
|
|
|
ChangesCalled bool
|
|
ChangesChanges *plans.ChangesSync
|
|
|
|
StateCalled bool
|
|
StateState *states.SyncState
|
|
|
|
ChecksCalled bool
|
|
ChecksState *checks.State
|
|
|
|
RefreshStateCalled bool
|
|
RefreshStateState *states.SyncState
|
|
|
|
PrevRunStateCalled bool
|
|
PrevRunStateState *states.SyncState
|
|
|
|
MoveResultsCalled bool
|
|
MoveResultsResults refactoring.MoveResults
|
|
|
|
InstanceExpanderCalled bool
|
|
InstanceExpanderExpander *instances.Expander
|
|
}
|
|
|
|
// MockEvalContext implements EvalContext
|
|
var _ EvalContext = (*MockEvalContext)(nil)
|
|
|
|
func (c *MockEvalContext) Stopped() <-chan struct{} {
|
|
c.StoppedCalled = true
|
|
return c.StoppedValue
|
|
}
|
|
|
|
func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
|
|
c.HookCalled = true
|
|
if c.HookHook != nil {
|
|
if _, err := fn(c.HookHook); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return c.HookError
|
|
}
|
|
|
|
func (c *MockEvalContext) Input() UIInput {
|
|
c.InputCalled = true
|
|
return c.InputInput
|
|
}
|
|
|
|
func (c *MockEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) {
|
|
c.InitProviderCalled = true
|
|
c.InitProviderType = addr.String()
|
|
c.InitProviderAddr = addr
|
|
return c.InitProviderProvider, c.InitProviderError
|
|
}
|
|
|
|
func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface {
|
|
c.ProviderCalled = true
|
|
c.ProviderAddr = addr
|
|
return c.ProviderProvider
|
|
}
|
|
|
|
func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) {
|
|
c.ProviderSchemaCalled = true
|
|
c.ProviderSchemaAddr = addr
|
|
return c.ProviderSchemaSchema, c.ProviderSchemaError
|
|
}
|
|
|
|
func (c *MockEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
|
|
c.CloseProviderCalled = true
|
|
c.CloseProviderAddr = addr
|
|
return nil
|
|
}
|
|
|
|
func (c *MockEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
|
|
|
|
c.ConfigureProviderCalled = true
|
|
c.ConfigureProviderAddr = addr
|
|
c.ConfigureProviderConfig = cfg
|
|
if c.ConfigureProviderFn != nil {
|
|
return c.ConfigureProviderFn(addr, cfg)
|
|
}
|
|
return c.ConfigureProviderDiags
|
|
}
|
|
|
|
func (c *MockEvalContext) ProviderInput(addr addrs.AbsProviderConfig) map[string]cty.Value {
|
|
c.ProviderInputCalled = true
|
|
c.ProviderInputAddr = addr
|
|
return c.ProviderInputValues
|
|
}
|
|
|
|
func (c *MockEvalContext) SetProviderInput(addr addrs.AbsProviderConfig, vals map[string]cty.Value) {
|
|
c.SetProviderInputCalled = true
|
|
c.SetProviderInputAddr = addr
|
|
c.SetProviderInputValues = vals
|
|
}
|
|
|
|
func (c *MockEvalContext) Provisioner(n string) (provisioners.Interface, error) {
|
|
c.ProvisionerCalled = true
|
|
c.ProvisionerName = n
|
|
return c.ProvisionerProvisioner, nil
|
|
}
|
|
|
|
func (c *MockEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
|
|
c.ProvisionerSchemaCalled = true
|
|
c.ProvisionerSchemaName = n
|
|
return c.ProvisionerSchemaSchema, c.ProvisionerSchemaError
|
|
}
|
|
|
|
func (c *MockEvalContext) CloseProvisioners() error {
|
|
c.CloseProvisionersCalled = true
|
|
return nil
|
|
}
|
|
|
|
func (c *MockEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
|
|
c.EvaluateBlockCalled = true
|
|
c.EvaluateBlockBody = body
|
|
c.EvaluateBlockSchema = schema
|
|
c.EvaluateBlockSelf = self
|
|
c.EvaluateBlockKeyData = keyData
|
|
if c.EvaluateBlockResultFunc != nil {
|
|
return c.EvaluateBlockResultFunc(body, schema, self, keyData)
|
|
}
|
|
return c.EvaluateBlockResult, c.EvaluateBlockExpandedBody, c.EvaluateBlockDiags
|
|
}
|
|
|
|
func (c *MockEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
|
|
c.EvaluateExprCalled = true
|
|
c.EvaluateExprExpr = expr
|
|
c.EvaluateExprWantType = wantType
|
|
c.EvaluateExprSelf = self
|
|
if c.EvaluateExprResultFunc != nil {
|
|
return c.EvaluateExprResultFunc(expr, wantType, self)
|
|
}
|
|
return c.EvaluateExprResult, c.EvaluateExprDiags
|
|
}
|
|
|
|
func (c *MockEvalContext) EvaluateReplaceTriggeredBy(hcl.Expression, instances.RepetitionData) (*addrs.Reference, bool, tfdiags.Diagnostics) {
|
|
return nil, false, nil
|
|
}
|
|
|
|
// 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, keyData InstanceKeyEvalData) (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, keyData InstanceKeyEvalData) *lang.Scope {
|
|
c.EvaluationScopeCalled = true
|
|
c.EvaluationScopeSelf = self
|
|
c.EvaluationScopeKeyData = keyData
|
|
return c.EvaluationScopeScope
|
|
}
|
|
|
|
func (c *MockEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
|
|
newC := *c
|
|
newC.PathPath = path
|
|
return &newC
|
|
}
|
|
|
|
func (c *MockEvalContext) Path() addrs.ModuleInstance {
|
|
c.PathCalled = true
|
|
return c.PathPath
|
|
}
|
|
|
|
func (c *MockEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) {
|
|
c.SetRootModuleArgumentCalled = true
|
|
c.SetRootModuleArgumentAddr = addr
|
|
c.SetRootModuleArgumentValue = v
|
|
if c.SetRootModuleArgumentFunc != nil {
|
|
c.SetRootModuleArgumentFunc(addr, v)
|
|
}
|
|
}
|
|
|
|
func (c *MockEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) {
|
|
c.SetModuleCallArgumentCalled = true
|
|
c.SetModuleCallArgumentModuleCall = callAddr
|
|
c.SetModuleCallArgumentVariable = varAddr
|
|
c.SetModuleCallArgumentValue = v
|
|
if c.SetModuleCallArgumentFunc != nil {
|
|
c.SetModuleCallArgumentFunc(callAddr, varAddr, v)
|
|
}
|
|
}
|
|
|
|
func (c *MockEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value {
|
|
c.GetVariableValueCalled = true
|
|
c.GetVariableValueAddr = addr
|
|
if c.GetVariableValueFunc != nil {
|
|
return c.GetVariableValueFunc(addr)
|
|
}
|
|
return c.GetVariableValueValue
|
|
}
|
|
|
|
func (c *MockEvalContext) Changes() *plans.ChangesSync {
|
|
c.ChangesCalled = true
|
|
return c.ChangesChanges
|
|
}
|
|
|
|
func (c *MockEvalContext) State() *states.SyncState {
|
|
c.StateCalled = true
|
|
return c.StateState
|
|
}
|
|
|
|
func (c *MockEvalContext) Checks() *checks.State {
|
|
c.ChecksCalled = true
|
|
return c.ChecksState
|
|
}
|
|
|
|
func (c *MockEvalContext) RefreshState() *states.SyncState {
|
|
c.RefreshStateCalled = true
|
|
return c.RefreshStateState
|
|
}
|
|
|
|
func (c *MockEvalContext) PrevRunState() *states.SyncState {
|
|
c.PrevRunStateCalled = true
|
|
return c.PrevRunStateState
|
|
}
|
|
|
|
func (c *MockEvalContext) MoveResults() refactoring.MoveResults {
|
|
c.MoveResultsCalled = true
|
|
return c.MoveResultsResults
|
|
}
|
|
|
|
func (c *MockEvalContext) InstanceExpander() *instances.Expander {
|
|
c.InstanceExpanderCalled = true
|
|
return c.InstanceExpanderExpander
|
|
}
|