Skip variable validations during destroy apply operations (#34101)

* skip variable validations during destroy plans

* fix missing destroying field

* only skip validations during the apply stage
pull/34113/head
Liam Cervante 3 years ago committed by GitHub
parent 493056d828
commit e08d29673f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2163,6 +2163,86 @@ import {
}
}
func TestContext2Apply_destroySkipsVariableValidations(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
variable "input" {
type = string
validation {
condition = var.input == "foo"
error_message = "bad input"
}
}
resource "test_object" "a" {
test_string = var.input
}
`,
})
p := simpleMockProvider()
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan(m, states.BuildState(func(state *states.SyncState) {
state.SetResourceInstanceCurrent(
mustResourceInstanceAddr("test_object.a"),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"test_string":"foo"}`),
},
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
)
}), &PlanOpts{
Mode: plans.DestroyMode,
SetVariables: InputValues{
"input": {
Value: cty.StringVal("foo"),
SourceType: ValueFromCLIArg,
SourceRange: tfdiags.SourceRange{},
},
},
})
if diags.HasErrors() {
t.Errorf("expected no errors, but got %s", diags)
}
planResult := plan.Checks.GetObjectResult(addrs.AbsInputVariableInstance{
Variable: addrs.InputVariable{
Name: "input",
},
Module: addrs.RootModuleInstance,
})
if planResult.Status != checks.StatusPass {
// Should have passed during the planning stage indicating that it did
// actually execute.
t.Errorf("expected checks to be pass but was %s", planResult.Status)
}
state, diags := ctx.Apply(plan, m)
if diags.HasErrors() {
t.Errorf("expected no errors, but got %s", diags)
}
applyResult := state.CheckResults.GetObjectResult(addrs.AbsInputVariableInstance{
Variable: addrs.InputVariable{
Name: "input",
},
Module: addrs.RootModuleInstance,
})
if applyResult.Status != checks.StatusUnknown {
// Shouldn't have made any validations here, so result should have
// stayed as unknown.
t.Errorf("expected checks to be unknown but was %s", applyResult.Status)
}
}
func TestContext2Apply_noExternalReferences(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `

@ -100,8 +100,15 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
},
// Add dynamic values
&RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues},
&ModuleVariableTransformer{Config: b.Config},
&RootVariableTransformer{
Config: b.Config,
RawValues: b.RootVariableValues,
DestroyApply: b.Operation == walkDestroy,
},
&ModuleVariableTransformer{
Config: b.Config,
DestroyApply: b.Operation == walkDestroy,
},
&LocalTransformer{Config: b.Config},
&OutputTransformer{
Config: b.Config,

@ -128,8 +128,17 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
},
// Add dynamic values
&RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues, Planning: true},
&ModuleVariableTransformer{Config: b.Config, Planning: true},
&RootVariableTransformer{
Config: b.Config,
RawValues: b.RootVariableValues,
Planning: true,
DestroyApply: false, // always false for planning
},
&ModuleVariableTransformer{
Config: b.Config,
Planning: true,
DestroyApply: false, // always false for planning
},
&LocalTransformer{Config: b.Config},
&OutputTransformer{
Config: b.Config,

@ -29,6 +29,10 @@ type nodeExpandModuleVariable struct {
// Planning must be set to true when building a planning graph, and must be
// false when building an apply graph.
Planning bool
// DestroyApply must be set to true when planning or applying a destroy
// operation, and false otherwise.
DestroyApply bool
}
var (
@ -72,6 +76,7 @@ func (n *nodeExpandModuleVariable) DynamicExpand(ctx EvalContext) (*Graph, tfdia
Config: n.Config,
Expr: n.Expr,
ModuleInstance: module,
DestroyApply: n.DestroyApply,
}
g.Add(o)
}
@ -140,6 +145,10 @@ type nodeModuleVariable struct {
// ModuleInstance in order to create the appropriate context for evaluating
// ModuleCallArguments, ex. so count.index and each.key can resolve
ModuleInstance addrs.ModuleInstance
// DestroyApply must be set to true when applying a destroy operation and
// false otherwise.
DestroyApply bool
}
// Ensure that we are implementing all of the interfaces we think we are
@ -195,7 +204,14 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags t
_, call := n.Addr.Module.CallInstance()
ctx.SetModuleCallArgument(call, n.Addr.Variable, val)
return evalVariableValidations(n.Addr, n.Config, n.Expr, ctx)
// Skip evalVariableValidations during destroy operations. We still want
// to evaluate the variable in case it is used to initialise providers
// or something downstream but we don't need to report on the success
// or failure of any validations for destroy operations.
if !n.DestroyApply {
diags = diags.Append(evalVariableValidations(n.Addr, n.Config, n.Expr, ctx))
}
return diags
}
// dag.GraphNodeDotter impl.

@ -29,6 +29,10 @@ type NodeRootVariable struct {
// Planning must be set to true when building a planning graph, and must be
// false when building an apply graph.
Planning bool
// DestroyApply must be set to true when applying a destroy operation and
// false otherwise.
DestroyApply bool
}
var (
@ -109,13 +113,15 @@ func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Di
ctx.SetRootModuleArgument(addr.Variable, finalVal)
moreDiags = evalVariableValidations(
addrs.RootModuleInstance.InputVariable(n.Addr.Name),
n.Config,
nil, // not set for root module variables
ctx,
)
diags = diags.Append(moreDiags)
if !n.DestroyApply {
diags = diags.Append(evalVariableValidations(
addrs.RootModuleInstance.InputVariable(n.Addr.Name),
n.Config,
nil, // not set for root module variables
ctx,
))
}
return diags
}

@ -32,6 +32,10 @@ type ModuleVariableTransformer struct {
// Planning must be set to true when building a planning graph, and must be
// false when building an apply graph.
Planning bool
// DestroyApply must be set to true when applying a destroy operation and
// false otherwise.
DestroyApply bool
}
func (t *ModuleVariableTransformer) Transform(g *Graph) error {
@ -110,10 +114,11 @@ func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs
Addr: addrs.InputVariable{
Name: v.Name,
},
Module: c.Path,
Config: v,
Expr: expr,
Planning: t.Planning,
Module: c.Path,
Config: v,
Expr: expr,
Planning: t.Planning,
DestroyApply: t.DestroyApply,
}
g.Add(node)
}

@ -22,6 +22,10 @@ type RootVariableTransformer struct {
// Planning must be set to true when building a planning graph, and must be
// false when building an apply graph.
Planning bool
// DestroyApply must be set to true when applying a destroy operation and
// false otherwise.
DestroyApply bool
}
func (t *RootVariableTransformer) Transform(g *Graph) error {
@ -40,9 +44,10 @@ func (t *RootVariableTransformer) Transform(g *Graph) error {
Addr: addrs.InputVariable{
Name: v.Name,
},
Config: v,
RawValue: t.RawValues[v.Name],
Planning: t.Planning,
Config: v,
RawValue: t.RawValues[v.Name],
Planning: t.Planning,
DestroyApply: t.DestroyApply,
}
g.Add(node)
}

Loading…
Cancel
Save