mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
192 lines
8.4 KiB
Go
192 lines
8.4 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package statemgr
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
version "github.com/hashicorp/go-version"
|
|
|
|
"github.com/hashicorp/terraform/internal/schemarepo"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
)
|
|
|
|
// Persistent is a union of the Refresher and Persistent interfaces, for types
|
|
// that deal with persistent snapshots.
|
|
//
|
|
// Persistent snapshots are ones that are retained in storage that will
|
|
// outlive a particular Terraform process, and are shared with other Terraform
|
|
// processes that have a similarly-configured state manager.
|
|
//
|
|
// A manager may also choose to retain historical persistent snapshots, but
|
|
// that is an implementation detail and not visible via this API.
|
|
type Persistent interface {
|
|
Refresher
|
|
Persister
|
|
OutputReader
|
|
}
|
|
|
|
// OutputReader is the interface for managers that fetches output values from state
|
|
// or another source. This is a refinement of fetching the entire state and digging
|
|
// the output values from it because operations backends can apply special permissions
|
|
// to differentiate reading the state and reading the outputs within the state.
|
|
type OutputReader interface {
|
|
// GetRootOutputValues fetches the root module output values from state or another source
|
|
GetRootOutputValues(ctx context.Context) (map[string]*states.OutputValue, error)
|
|
}
|
|
|
|
// Refresher is the interface for managers that can read snapshots from
|
|
// persistent storage.
|
|
//
|
|
// Refresher is usually implemented in conjunction with Reader, with
|
|
// RefreshState copying the latest persistent snapshot into the latest
|
|
// transient snapshot.
|
|
//
|
|
// For a type that implements both Refresher and Persister, RefreshState must
|
|
// return the result of the most recently completed successful call to
|
|
// PersistState, unless another concurrently-running process has persisted
|
|
// another snapshot in the mean time.
|
|
//
|
|
// The Refresher implementation must guarantee that the snapshot is read
|
|
// from persistent storage in a way that is safe under concurrent calls to
|
|
// PersistState that may be happening in other processes.
|
|
type Refresher interface {
|
|
// RefreshState retrieves a snapshot of state from persistent storage,
|
|
// returning an error if this is not possible.
|
|
//
|
|
// Types that implement RefreshState generally also implement a State
|
|
// method that returns the result of the latest successful refresh.
|
|
//
|
|
// Since only a subset of the data in a state is included when persisting,
|
|
// a round-trip through PersistState and then RefreshState will often
|
|
// return only a subset of what was written. Callers must assume that
|
|
// ephemeral portions of the state may be unpopulated after calling
|
|
// RefreshState.
|
|
RefreshState() error
|
|
}
|
|
|
|
// Persister is the interface for managers that can write snapshots to
|
|
// persistent storage.
|
|
//
|
|
// Persister is usually implemented in conjunction with Writer, with
|
|
// PersistState copying the latest transient snapshot to be the new latest
|
|
// persistent snapshot.
|
|
//
|
|
// A Persister implementation must detect updates made by other processes
|
|
// that may be running concurrently and avoid destroying those changes. This
|
|
// is most commonly achieved by making use of atomic write capabilities on
|
|
// the remote storage backend in conjunction with book-keeping with the
|
|
// Serial and Lineage fields in the standard state file formats.
|
|
//
|
|
// Some implementations may optionally utilize config schema to persist
|
|
// state. For example, when representing state in an external JSON
|
|
// representation.
|
|
type Persister interface {
|
|
PersistState(*schemarepo.Schemas) error
|
|
}
|
|
|
|
// PersistentMeta is an optional extension to Persistent that allows inspecting
|
|
// the metadata associated with the snapshot that was most recently either
|
|
// read by RefreshState or written by PersistState.
|
|
type PersistentMeta interface {
|
|
// StateSnapshotMeta returns metadata about the state snapshot most
|
|
// recently created either by a call to PersistState or read by a call
|
|
// to RefreshState.
|
|
//
|
|
// If no persistent snapshot is yet available in the manager then
|
|
// the return value is meaningless. This method is primarily available
|
|
// for testing and logging purposes, and is of little use otherwise.
|
|
StateSnapshotMeta() SnapshotMeta
|
|
}
|
|
|
|
// SnapshotMeta contains metadata about a persisted state snapshot.
|
|
//
|
|
// This metadata is usually (but not necessarily) included as part of the
|
|
// "header" of a state file, which is then written to a raw blob storage medium
|
|
// by a persistent state manager.
|
|
//
|
|
// Not all state managers will have useful values for all fields in this
|
|
// struct, so SnapshotMeta values are of little use beyond testing and logging
|
|
// use-cases.
|
|
type SnapshotMeta struct {
|
|
// Lineage and Serial can be used to understand the relationships between
|
|
// snapshots.
|
|
//
|
|
// If two snapshots both have an identical, non-empty Lineage
|
|
// then the one with the higher Serial is newer than the other.
|
|
// If the Lineage values are different or empty then the two snapshots
|
|
// are unrelated and cannot be compared for relative age.
|
|
Lineage string
|
|
Serial uint64
|
|
|
|
// TerraformVersion is the number of the version of Terraform that created
|
|
// the snapshot.
|
|
TerraformVersion *version.Version
|
|
}
|
|
|
|
// IntermediateStateConditionalPersister is an optional extension of
|
|
// [Persister] that allows an implementation to tailor the rules for
|
|
// whether to create intermediate state snapshots when Terraform Core emits
|
|
// events reporting that the state might have changed. This interface is used
|
|
// by the local backend when it's been configured to use another backend for
|
|
// state storage.
|
|
//
|
|
// For state managers that don't implement this interface, the local backend's
|
|
// StateHook uses a default set of rules that aim to be a good compromise
|
|
// between how long a state change can be active before it gets committed as a
|
|
// snapshot vs. how many intermediate snapshots will get created. That
|
|
// compromise is subject to change over time, but a state manager can implement
|
|
// this interface to exert full control over those rules.
|
|
type IntermediateStateConditionalPersister interface {
|
|
// ShouldPersistIntermediateState will be called each time Terraform Core
|
|
// emits an intermediate state event that is potentially eligible to be
|
|
// persisted.
|
|
//
|
|
// The implemention should return true to signal that the state snapshot
|
|
// most recently provided to the object's WriteState should be persisted,
|
|
// or false if it should not be persisted. If this function returns true
|
|
// then the receiver will see a subsequent call to
|
|
// [statemgr.Persister.PersistState] to request persistence.
|
|
//
|
|
// The implementation must not modify anything reachable through the
|
|
// arguments, and must not retain pointers to anything reachable through
|
|
// them after the function returns. However, implementers can assume that
|
|
// nothing will write to anything reachable through the arguments while
|
|
// this function is active.
|
|
ShouldPersistIntermediateState(info *IntermediateStatePersistInfo) bool
|
|
}
|
|
|
|
type IntermediateStatePersistInfo struct {
|
|
// RequestedPersistInterval is the persist interval requested by whatever
|
|
// instantiated the StateHook.
|
|
//
|
|
// Implementations of [IntermediateStateConditionalPersister] should ideally
|
|
// respect this, but may ignore it if they use something other than the
|
|
// passage of time to make their decision.
|
|
RequestedPersistInterval time.Duration
|
|
|
|
// LastPersist is the time when the last intermediate state snapshot was
|
|
// persisted, or the time of the first report for Terraform Core if there
|
|
// hasn't yet been a persisted snapshot.
|
|
LastPersist time.Time
|
|
|
|
// ForcePersist is true when Terraform CLI has receieved an interrupt
|
|
// signal and is therefore trying to create snapshots more aggressively
|
|
// in anticipation of possibly being terminated ungracefully.
|
|
// [IntermediateStateConditionalPersister] implementations should ideally
|
|
// persist every snapshot they get when this flag is set, unless they have
|
|
// some external information that implies this shouldn't be necessary.
|
|
ForcePersist bool
|
|
}
|
|
|
|
// DefaultIntermediateStatePersistRule is the default implementation of
|
|
// [IntermediateStateConditionalPersister.ShouldPersistIntermediateState] used
|
|
// when the selected state manager doesn't implement that interface.
|
|
//
|
|
// Implementers of that interface can optionally wrap a call to this function
|
|
// if they want to combine the default behavior with some logic of their own.
|
|
func DefaultIntermediateStatePersistRule(info *IntermediateStatePersistInfo) bool {
|
|
return info.ForcePersist || time.Since(info.LastPersist) >= info.RequestedPersistInterval
|
|
}
|