Merge pull request #26192 from hashicorp/jbardin/lost-cbd
Use saved plan to determine CreateBeforeDestroy status
This commit is contained in:
commit
84438d377f
|
@ -11847,3 +11847,68 @@ resource "test_resource" "a" {
|
||||||
t.Fatalf("apply errors: %s", diags.Err())
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -59,6 +59,18 @@ func (n *EvalCheckPlannedChange) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
// all of the unknown values, since the final values might actually
|
// all of the unknown values, since the final values might actually
|
||||||
// match what was there before after all.
|
// 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)
|
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:
|
default:
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
|
@ -118,6 +130,12 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
provider := *n.Provider
|
provider := *n.Provider
|
||||||
providerSchema := *n.ProviderSchema
|
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 {
|
if providerSchema == nil {
|
||||||
return nil, fmt.Errorf("provider schema is unavailable for %s", n.Addr)
|
return nil, fmt.Errorf("provider schema is unavailable for %s", n.Addr)
|
||||||
}
|
}
|
||||||
|
@ -388,7 +406,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
case !reqRep.Empty():
|
case !reqRep.Empty():
|
||||||
// If there are any "requires replace" paths left _after our filtering
|
// If there are any "requires replace" paths left _after our filtering
|
||||||
// above_ then this is a replace action.
|
// above_ then this is a replace action.
|
||||||
if n.CreateBeforeDestroy {
|
if createBeforeDestroy {
|
||||||
action = plans.CreateThenDelete
|
action = plans.CreateThenDelete
|
||||||
} else {
|
} else {
|
||||||
action = plans.DeleteThenCreate
|
action = plans.DeleteThenCreate
|
||||||
|
@ -454,7 +472,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
// as a replace change, even though so far we've been treating it as a
|
// as a replace change, even though so far we've been treating it as a
|
||||||
// create.
|
// create.
|
||||||
if action == plans.Create && priorValTainted != cty.NilVal {
|
if action == plans.Create && priorValTainted != cty.NilVal {
|
||||||
if n.CreateBeforeDestroy {
|
if createBeforeDestroy {
|
||||||
action = plans.CreateThenDelete
|
action = plans.CreateThenDelete
|
||||||
} else {
|
} else {
|
||||||
action = plans.DeleteThenCreate
|
action = plans.DeleteThenCreate
|
||||||
|
|
|
@ -263,10 +263,15 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
|
||||||
destroy := false
|
destroy := false
|
||||||
if diffApply != nil {
|
if diffApply != nil {
|
||||||
destroy = (diffApply.Action == plans.Delete || diffApply.Action.IsReplace())
|
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() {
|
if destroy && n.CreateBeforeDestroy() {
|
||||||
createBeforeDestroyEnabled = true
|
createBeforeDestroyEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return createBeforeDestroyEnabled, nil
|
return createBeforeDestroyEnabled, nil
|
||||||
},
|
},
|
||||||
Then: &EvalDeposeState{
|
Then: &EvalDeposeState{
|
||||||
|
|
Loading…
Reference in New Issue