only check serial when applying the first plan

Ensure that we still check for a stale plan even when it was created
with no previous state.

Create separate errors for incorrect lineage vs incorrect serial.

To prevent confusion when applying a first plan multiple times, only
report it as a stale plan rather than different lineage.
This commit is contained in:
James Bardin 2021-10-13 17:07:26 -04:00
parent 87e852c832
commit 5ffa0839f9

View File

@ -290,14 +290,27 @@ func (b *Local) localRunForPlanFile(op *backend.Operation, pf *planfile.Reader,
// has changed since the plan was created. (All of the "real-world" // has changed since the plan was created. (All of the "real-world"
// state manager implementations support this, but simpler test backends // state manager implementations support this, but simpler test backends
// may not.) // may not.)
if currentStateMeta.Lineage != "" && priorStateFile.Lineage != "" {
if priorStateFile.Serial != currentStateMeta.Serial || priorStateFile.Lineage != currentStateMeta.Lineage { // Because the plan always contains a state, even if it is empty, the
diags = diags.Append(tfdiags.Sourceless( // first plan to be applied will have empty snapshot metadata. In this
tfdiags.Error, // case we compare only the serial in order to provide a more correct
"Saved plan is stale", // error.
"The given plan file can no longer be applied because the state was changed by another operation after the plan was created.", firstPlan := priorStateFile.Lineage == "" && priorStateFile.Serial == 0
))
} switch {
case !firstPlan && priorStateFile.Lineage != currentStateMeta.Lineage:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Saved plan does not match the given state",
"The given plan file can not be applied because it was created from a different state lineage.",
))
case priorStateFile.Serial != currentStateMeta.Serial:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Saved plan is stale",
"The given plan file can no longer be applied because the state was changed by another operation after the plan was created.",
))
} }
} }
// When we're applying a saved plan, the input state is the "prior state" // When we're applying a saved plan, the input state is the "prior state"