Skip to content

Commit

Permalink
Merge pull request #26231 from hashicorp/jbardin/backport-26192
Browse files Browse the repository at this point in the history
Backport 26192
  • Loading branch information
jbardin authored Sep 14, 2020
2 parents 5406f3b + a16e617 commit a784cbe
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 2 deletions.
65 changes: 65 additions & 0 deletions terraform/context_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11847,3 +11847,68 @@ resource "test_resource" "a" {
t.Fatalf("apply errors: %s", diags.Err())
}
}

func TestContext2Apply_forcedCBD(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
variable "v" {}
resource "test_instance" "a" {
require_new = var.v
}
resource "test_instance" "b" {
depends_on = [test_instance.a]
lifecycle {
create_before_destroy = true
}
}
`})

p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn

ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
Variables: InputValues{
"v": &InputValue{
Value: cty.StringVal("A"),
},
},
})

if _, diags := ctx.Plan(); diags.HasErrors() {
t.Fatalf("plan errors: %s", diags.Err())
}

state, diags := ctx.Apply()
if diags.HasErrors() {
t.Fatalf("apply errors: %s", diags.Err())
}

ctx = testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
Variables: InputValues{
"v": &InputValue{
Value: cty.StringVal("B"),
},
},
State: state,
})

if _, diags := ctx.Plan(); diags.HasErrors() {
t.Fatalf("plan errors: %s", diags.Err())
}

_, diags = ctx.Apply()
if diags.HasErrors() {
t.Fatalf("apply errors: %s", diags.Err())
}
}
22 changes: 20 additions & 2 deletions terraform/eval_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ func (n *EvalCheckPlannedChange) Eval(ctx EvalContext) (interface{}, error) {
// all of the unknown values, since the final values might actually
// match what was there before after all.
log.Printf("[DEBUG] After incorporating new values learned so far during apply, %s change has become NoOp", absAddr)

case (plannedChange.Action == plans.CreateThenDelete && actualChange.Action == plans.DeleteThenCreate) ||
(plannedChange.Action == plans.DeleteThenCreate && actualChange.Action == plans.CreateThenDelete):
// If the order of replacement changed, then that is a bug in terraform
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Terraform produced inconsistent final plan",
fmt.Sprintf(
"When expanding the plan for %s to include new values learned so far during apply, the planned action changed from %s to %s.\n\nThis is a bug in Terraform and should be reported.",
absAddr, plannedChange.Action, actualChange.Action,
),
))
default:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
Expand Down Expand Up @@ -118,6 +130,12 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
provider := *n.Provider
providerSchema := *n.ProviderSchema

createBeforeDestroy := n.CreateBeforeDestroy
if n.PreviousDiff != nil {
// If we already planned the action, we stick to that plan
createBeforeDestroy = (*n.PreviousDiff).Action == plans.CreateThenDelete
}

if providerSchema == nil {
return nil, fmt.Errorf("provider schema is unavailable for %s", n.Addr)
}
Expand Down Expand Up @@ -372,7 +390,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
case !reqRep.Empty():
// If there are any "requires replace" paths left _after our filtering
// above_ then this is a replace action.
if n.CreateBeforeDestroy {
if createBeforeDestroy {
action = plans.CreateThenDelete
} else {
action = plans.DeleteThenCreate
Expand Down Expand Up @@ -438,7 +456,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
// as a replace change, even though so far we've been treating it as a
// create.
if action == plans.Create && priorValTainted != cty.NilVal {
if n.CreateBeforeDestroy {
if createBeforeDestroy {
action = plans.CreateThenDelete
} else {
action = plans.DeleteThenCreate
Expand Down
5 changes: 5 additions & 0 deletions terraform/node_resource_apply_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,15 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
destroy := false
if diffApply != nil {
destroy = (diffApply.Action == plans.Delete || diffApply.Action.IsReplace())

// Get the stored action for CBD if we have a plan already
createBeforeDestroyEnabled = diffApply.Change.Action == plans.CreateThenDelete
}

if destroy && n.CreateBeforeDestroy() {
createBeforeDestroyEnabled = true
}

return createBeforeDestroyEnabled, nil
},
Then: &EvalDeposeState{
Expand Down

0 comments on commit a784cbe

Please sign in to comment.