opentofu/internal/states/statemgr/plan.go
Martin Atkins f40800b3a4 Move states/ to internal/states/
This is part of a general effort to move all of Terraform's non-library
package surface under internal in order to reinforce that these are for
internal use within Terraform only.

If you were previously importing packages under this prefix into an
external codebase, you could pin to an earlier release tag as an interim
solution until you've make a plan to achieve the same functionality some
other way.
2021-05-17 14:09:07 -07:00

72 lines
2.7 KiB
Go

package statemgr
import (
"fmt"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/states/statefile"
)
// PlannedStateUpdate is a special helper to obtain a statefile representation
// of a not-yet-written state snapshot that can be written later by a call
// to the companion function WritePlannedStateUpdate.
//
// The statefile object returned here has an unusual interpretation of its
// metadata that is understood only by WritePlannedStateUpdate, and so the
// returned object should not be used for any other purpose.
//
// If the state manager implements Locker then it is the caller's
// responsibility to hold the lock at least for the duration of this call.
// It is not safe to modify the given state concurrently while
// PlannedStateUpdate is running.
func PlannedStateUpdate(mgr Transient, planned *states.State) *statefile.File {
ret := &statefile.File{
State: planned.DeepCopy(),
}
// If the given manager uses snapshot metadata then we'll save that
// in our file so we can check it again during WritePlannedStateUpdate.
if mr, ok := mgr.(PersistentMeta); ok {
m := mr.StateSnapshotMeta()
ret.Lineage = m.Lineage
ret.Serial = m.Serial
}
return ret
}
// WritePlannedStateUpdate is a companion to PlannedStateUpdate that attempts
// to apply a state update that was planned earlier to the given state
// manager.
//
// An error is returned if this function detects that a new state snapshot
// has been written to the backend since the update was planned, since that
// invalidates the plan. An error is returned also if the manager itself
// rejects the given state when asked to store it.
//
// If the returned error is nil, the given manager's transient state snapshot
// is updated to match what was planned. It is the caller's responsibility
// to then persist that state if the manager also implements Persistent and
// the snapshot should be written to the persistent store.
//
// If the state manager implements Locker then it is the caller's
// responsibility to hold the lock at least for the duration of this call.
func WritePlannedStateUpdate(mgr Transient, planned *statefile.File) error {
// If the given manager uses snapshot metadata then we'll check to make
// sure no new snapshots have been created since we planned to write
// the given state file.
if mr, ok := mgr.(PersistentMeta); ok {
m := mr.StateSnapshotMeta()
if planned.Lineage != "" {
if planned.Lineage != m.Lineage {
return fmt.Errorf("planned state update is from an unrelated state lineage than the current state")
}
if planned.Serial != m.Serial {
return fmt.Errorf("stored state has been changed by another operation since the given update was planned")
}
}
}
return mgr.WriteState(planned.State)
}