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())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -388,7 +406,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
|
||||
|
@ -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
|
||||
// create.
|
||||
if action == plans.Create && priorValTainted != cty.NilVal {
|
||||
if n.CreateBeforeDestroy {
|
||||
if createBeforeDestroy {
|
||||
action = plans.CreateThenDelete
|
||||
} else {
|
||||
action = plans.DeleteThenCreate
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Reference in New Issue