Merge pull request #23717 from hashicorp/jbardin/destroy-plan-values

Always prune unused values
This commit is contained in:
James Bardin 2020-01-07 17:06:20 -05:00 committed by GitHub
commit 8b0888798f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 22 deletions

View File

@ -10526,7 +10526,6 @@ func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) {
} }
ctxOpts.ProviderResolver = providerResolver ctxOpts.ProviderResolver = providerResolver
ctxOpts.Destroy = true
ctx, diags = NewContext(ctxOpts) ctx, diags = NewContext(ctxOpts)
if diags.HasErrors() { if diags.HasErrors() {
t.Fatalf("err: %s", diags.Err()) t.Fatalf("err: %s", diags.Err())

View File

@ -175,17 +175,20 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
// Reverse the edges from outputs and locals, so that // Reverse the edges from outputs and locals, so that
// interpolations don't fail during destroy. // interpolations don't fail during destroy.
// Create a destroy node for outputs to remove them from the state. // Create a destroy node for outputs to remove them from the state.
// Prune unreferenced values, which may have interpolations that can't
// be resolved.
GraphTransformIf( GraphTransformIf(
func() bool { return b.Destroy }, func() bool { return b.Destroy },
GraphTransformMulti( GraphTransformMulti(
&DestroyValueReferenceTransformer{}, &DestroyValueReferenceTransformer{},
&DestroyOutputTransformer{}, &DestroyOutputTransformer{},
&PruneUnusedValuesTransformer{},
), ),
), ),
// Prune unreferenced values, which may have interpolations that can't
// be resolved.
&PruneUnusedValuesTransformer{
Destroy: b.Destroy,
},
// Add the node to fix the state count boundaries // Add the node to fix the state count boundaries
&CountBoundaryTransformer{ &CountBoundaryTransformer{
Config: b.Config, Config: b.Config,

View File

@ -406,9 +406,7 @@ module.child:
<no state> <no state>
Outputs: Outputs:
aws_access_key = YYYYY
aws_route53_zone_id = XXXX aws_route53_zone_id = XXXX
aws_secret_key = ZZZZ
` `
const testTerraformApplyDependsCreateBeforeStr = ` const testTerraformApplyDependsCreateBeforeStr = `
@ -693,11 +691,6 @@ foo = bar
const testTerraformApplyOutputOrphanModuleStr = ` const testTerraformApplyOutputOrphanModuleStr = `
<no state> <no state>
module.child:
<no state>
Outputs:
foo = bar
` `
const testTerraformApplyProvisionerStr = ` const testTerraformApplyProvisionerStr = `

View File

@ -3,9 +3,18 @@ variable "list" {
} }
resource "aws_instance" "a" { resource "aws_instance" "a" {
count = "${length(var.list)}" count = length(var.list)
}
locals {
ids = aws_instance.a[*].id
}
module "empty" {
source = "./mod"
input = zipmap(var.list, local.ids)
} }
output "out" { output "out" {
value = "${aws_instance.a.*.id}" value = aws_instance.a[*].id
} }

View File

@ -0,0 +1,2 @@
variable "input" {
}

View File

@ -206,21 +206,30 @@ func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error {
return nil return nil
} }
// PruneUnusedValuesTransformer is s GraphTransformer that removes local and // PruneUnusedValuesTransformer is a GraphTransformer that removes local,
// output values which are not referenced in the graph. Since outputs and // variable, and output values which are not referenced in the graph. If these
// locals always need to be evaluated, if they reference a resource that is not // values reference a resource that is no longer in the state the interpolation
// available in the state the interpolation could fail. // could fail.
type PruneUnusedValuesTransformer struct{} type PruneUnusedValuesTransformer struct {
Destroy bool
}
func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error { func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
// this might need multiple runs in order to ensure that pruning a value // Pruning a value can effect previously checked edges, so loop until there
// doesn't effect a previously checked value. // are no more changes.
for removed := 0; ; removed = 0 { for removed := 0; ; removed = 0 {
for _, v := range g.Vertices() { for _, v := range g.Vertices() {
switch v.(type) { switch v := v.(type) {
case *NodeApplyableOutput, *NodeLocal: case *NodeApplyableOutput:
// If we're not certain this is a full destroy, we need to keep any
// root module outputs
if v.Addr.Module.IsRoot() && !t.Destroy {
continue
}
case *NodeLocal, *NodeApplyableModuleVariable:
// OK // OK
default: default:
// We're only concerned with variables, locals and outputs
continue continue
} }
@ -229,6 +238,7 @@ func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
switch dependants.Len() { switch dependants.Len() {
case 0: case 0:
// nothing at all depends on this // nothing at all depends on this
log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
g.Remove(v) g.Remove(v)
removed++ removed++
case 1: case 1:
@ -236,6 +246,7 @@ func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error {
// we need to check for the case of a single destroy node. // we need to check for the case of a single destroy node.
d := dependants.List()[0] d := dependants.List()[0]
if _, ok := d.(*NodeDestroyableOutput); ok { if _, ok := d.(*NodeDestroyableOutput); ok {
log.Printf("[TRACE] PruneUnusedValuesTransformer: removing unused value %s", dag.VertexName(v))
g.Remove(v) g.Remove(v)
removed++ removed++
} }