backend/local: preserve serial and lineage on failure
When failing to write the state, the local backend writes the state to a local file called `errrored.tfstate`. Previously it would do so by creating a new state file which would use a new serial and lineage. By exorting the existing state file and directly assigning the new state, the serial and lineage are preserved.
This commit is contained in:
parent
4200b0b2c5
commit
57f6e01830
|
@ -157,7 +157,15 @@ func (b *Local) opApply(
|
|||
runningOp.State = applyState
|
||||
err := statemgr.WriteAndPersist(opState, applyState)
|
||||
if err != nil {
|
||||
diags = diags.Append(b.backupStateForError(applyState, err))
|
||||
// Export the state file from the state manager and assign the new
|
||||
// state. This is needed to preserve the existing serial and lineage.
|
||||
stateFile := statemgr.Export(opState)
|
||||
if stateFile == nil {
|
||||
stateFile = &statefile.File{}
|
||||
}
|
||||
stateFile.State = applyState
|
||||
|
||||
diags = diags.Append(b.backupStateForError(stateFile, err))
|
||||
b.ReportResult(runningOp, diags)
|
||||
return
|
||||
}
|
||||
|
@ -208,11 +216,11 @@ func (b *Local) opApply(
|
|||
// to local disk to help the user recover. This is a "last ditch effort" sort
|
||||
// of thing, so we really don't want to end up in this codepath; we should do
|
||||
// everything we possibly can to get the state saved _somewhere_.
|
||||
func (b *Local) backupStateForError(applyState *states.State, err error) error {
|
||||
func (b *Local) backupStateForError(stateFile *statefile.File, err error) error {
|
||||
b.CLI.Error(fmt.Sprintf("Failed to save state: %s\n", err))
|
||||
|
||||
local := statemgr.NewFilesystem("errored.tfstate")
|
||||
writeErr := local.WriteState(applyState)
|
||||
writeErr := local.WriteStateForMigration(stateFile, true)
|
||||
if writeErr != nil {
|
||||
b.CLI.Error(fmt.Sprintf(
|
||||
"Also failed to create local state file for recovery: %s\n\n", writeErr,
|
||||
|
@ -223,9 +231,6 @@ func (b *Local) backupStateForError(applyState *states.State, err error) error {
|
|||
// but at least the user has _some_ path to recover if we end up
|
||||
// here for some reason.
|
||||
stateBuf := new(bytes.Buffer)
|
||||
stateFile := &statefile.File{
|
||||
State: applyState,
|
||||
}
|
||||
jsonErr := statefile.Write(stateFile, stateBuf)
|
||||
if jsonErr != nil {
|
||||
b.CLI.Error(fmt.Sprintf(
|
||||
|
|
|
@ -49,7 +49,7 @@ type Filesystem struct {
|
|||
lockID string
|
||||
|
||||
// created is set to true if stateFileOut didn't exist before we created it.
|
||||
// This is mostly so we can clean up emtpy files during tests, but doesn't
|
||||
// This is mostly so we can clean up empty files during tests, but doesn't
|
||||
// hurt to remove file we never wrote to.
|
||||
created bool
|
||||
|
||||
|
|
Loading…
Reference in New Issue