core: EvalDiff must handle Create/Replace as a special case

When we re-run EvalDiff during apply, we may have already completed the
destroy leg of a replace operation, leaving us in a different situation
than we were when we made the original planned change.

Therefore as a special case we will allow a create to turn back into a
replace if there was an earlier diff that requested that.
This commit is contained in:
Martin Atkins 2018-09-14 18:09:17 -07:00
parent dea74f9048
commit e9e11955a8
2 changed files with 38 additions and 11 deletions

View File

@ -331,6 +331,21 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
priorVal = priorValTainted
}
// As a special case, if we have a previous diff (presumably from the plan
// phases, whereas we're now in the apply phase) and it was for a replace,
// we've already deleted the original object from state by the time we
// get here and so we would've ended up with a _create_ action this time,
// which we now need to paper over to get a result consistent with what
// we originally intended.
if n.PreviousDiff != nil {
prevChange := *n.PreviousDiff
if prevChange.Action == plans.Replace && action == plans.Create {
log.Printf("[TRACE] EvalDiff: %s treating Create change as Replace change to match with earlier plan", absAddr)
action = plans.Replace
priorVal = prevChange.Before
}
}
// Call post-refresh hook
if !n.Stub {
err := ctx.Hook(func(h Hook) (HookAction, error) {

View File

@ -260,6 +260,13 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
Output: &state,
},
// Get the saved diff
&EvalReadDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema,
Change: &diff,
},
// Make a new diff, in case we've learned new values in the state
// during apply which we can now incorporate.
&EvalDiff{
@ -269,18 +276,12 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
PreviousDiff: &diff,
OutputChange: &diffApply,
OutputValue: &configVal,
OutputState: &state,
},
// Get the saved diff
&EvalReadDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema,
Change: &diff,
},
// Compare the diffs
&EvalCheckPlannedChange{
Addr: addr.Resource,
@ -389,10 +390,21 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
// We clear the diff out here so that future nodes
// don't see a diff that is already complete. There
// is no longer a diff!
&EvalWriteDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema,
Change: nil,
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if diff.Action != plans.Replace {
return true, nil
}
if !n.createBeforeDestroy() {
return true, nil
}
return false, nil
},
Then: &EvalWriteDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema,
Change: nil,
},
},
&EvalApplyPost{