core: Error if provider apply result is inconsistent with plan action

If the plan called for us to delete but the result isn't null then that's
suspect, because it suggests the object wasn't deleted after all.
Likewise, no other apply action should cause the the result to be missing.

In order to avoid the confusing user experience that results in this case
(since it often looks like Terraform did nothing at all) we'll produce
some errors about it, but still update the state to reflect what the
provider returned anyway to allow for debugging and recovery.
This commit is contained in:
Martin Atkins 2018-09-11 16:30:59 -07:00
parent 532277b7cc
commit a595194657
1 changed files with 25 additions and 0 deletions

View File

@ -140,6 +140,31 @@ func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
newVal = cty.UnknownAsNull(newVal)
}
// If a provider returns a null or non-null object at the wrong time then
// we still want to save that but it often causes some confusing behaviors
// where it seems like Terraform is failing to take any action at all,
// so we'll generate some errors to draw attention to it.
if change.Action == plans.Delete && !newVal.IsNull() {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Provider returned invalid result object after apply",
fmt.Sprintf(
"After applying a %s plan, the provider returned a non-null object for %s. Destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save this errant object in the state for debugging and recovery.",
change.Action, n.Addr.Absolute(ctx.Path()),
),
))
}
if change.Action != plans.Delete && newVal.IsNull() {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Provider returned invalid result object after apply",
fmt.Sprintf(
"After applying a %s plan, the provider returned a null object for %s. Only destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository.",
change.Action, n.Addr.Absolute(ctx.Path()),
),
))
}
var newState *states.ResourceInstanceObject
if !newVal.IsNull() { // null value indicates that the object is deleted, so we won't set a new state in that case
newState = &states.ResourceInstanceObject{