Documentation
¶
Index ¶
- Constants
- func DefaultIntermediateStatePersistRule(info *IntermediateStatePersistInfo) bool
- func TestLocalProvider(t *testing.T, b *Local, name string, schema providers.ProviderSchema) *tofu.MockProvider
- func TestNewLocalNoDefault(enc encryption.StateEncryption) backend.Backend
- func TestNewLocalSingle(enc encryption.StateEncryption) backend.Backend
- type IntermediateStateConditionalPersister
- type IntermediateStatePersistInfo
- type Local
- func (b *Local) CLIInit(opts *backend.CLIOpts) error
- func (b *Local) ConfigSchema() *configschema.Block
- func (b *Local) Configure(ctx context.Context, obj cty.Value) tfdiags.Diagnostics
- func (b *Local) DeleteWorkspace(ctx context.Context, name string, force bool) error
- func (b *Local) LocalRun(ctx context.Context, stopCtx context.Context, op *backend.Operation) (*backend.LocalRun, statemgr.Full, tfdiags.Diagnostics)
- func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.RunningOperation, error)
- func (b *Local) PathsConflictWith(other *Local) bool
- func (b *Local) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics)
- func (b *Local) ServiceDiscoveryAliases() ([]backend.HostAlias, error)
- func (b *Local) StateMgr(ctx context.Context, name string) (statemgr.Full, error)
- func (b *Local) StatePaths(name string) (stateIn, stateOut, backupOut string)
- func (b *Local) Workspaces(ctx context.Context) ([]string, error)
- type StateHook
- type StatelessRun
- type TestLocalNoDefaultState
- type TestLocalSingleState
Constants ¶
const ( DefaultWorkspaceDir = "terraform.tfstate.d" DefaultWorkspaceFile = "environment" DefaultStateFilename = "terraform.tfstate" DefaultBackupExtension = ".backup" )
Variables ¶
This section is empty.
Functions ¶
func DefaultIntermediateStatePersistRule ¶
func DefaultIntermediateStatePersistRule(info *IntermediateStatePersistInfo) 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 TestLocalProvider ¶
func TestLocalProvider(t *testing.T, b *Local, name string, schema providers.ProviderSchema) *tofu.MockProvider
TestLocalProvider modifies the ContextOpts of the *Local parameter to have a provider with the given name.
func TestNewLocalNoDefault ¶
func TestNewLocalNoDefault(enc encryption.StateEncryption) backend.Backend
TestNewLocalNoDefault is a factory for creating a TestLocalNoDefaultState. This function matches the signature required for backend/init.
func TestNewLocalSingle ¶
func TestNewLocalSingle(enc encryption.StateEncryption) backend.Backend
TestNewLocalSingle is a factory for creating a TestLocalSingleState. This function matches the signature required for backend/init.
Types ¶
type IntermediateStateConditionalPersister ¶
type IntermediateStateConditionalPersister interface {
// ShouldPersistIntermediateState will be called each time OpenTofu Core
// emits an intermediate state event that is potentially eligible to be
// persisted.
//
// The implementation 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
}
IntermediateStateConditionalPersister is an optional extension of statemgr.Persister that allows an implementation to tailor the rules for whether to create intermediate state snapshots when OpenTofu Core emits events reporting that the state might have changed.
For state managers that don't implement this interface, 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 IntermediateStatePersistInfo ¶
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 OpenTofu Core if there
// hasn't yet been a persisted snapshot.
LastPersist time.Time
// ForcePersist is true when OpenTofu CLI has received 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
}
type Local ¶
type Local struct {
// The State* paths are set from the backend config, and may be left blank
// to use the defaults. If the actual paths for the local backend state are
// needed, use the StatePaths method.
//
// StatePath is the local path where state is read from.
//
// StateOutPath is the local path where the state will be written.
// If this is empty, it will default to StatePath.
//
// StateBackupPath is the local path where a backup file will be written.
// Set this to "-" to disable state backup.
//
// StateWorkspaceDir is the path to the folder containing data for
// non-default workspaces. This defaults to DefaultWorkspaceDir if not set.
StatePath string
StateOutPath string
StateBackupPath string
StateWorkspaceDir string
// The OverrideState* paths are set based on per-operation CLI arguments
// and will override what'd be built from the State* fields if non-empty.
// While the interpretation of the State* fields depends on the active
// workspace, the OverrideState* fields are always used literally.
OverrideStatePath string
OverrideStateOutPath string
OverrideStateBackupPath string
// OpenTofu context. Many of these will be overridden or merged by
// Operation. See Operation for more details.
ContextOpts *tofu.ContextOpts
// OpInput will ask for necessary input prior to performing any operations.
//
// OpValidation will perform validation prior to running an operation. The
// variable naming doesn't match the style of others since we have a func
// Validate.
OpInput bool
OpValidation bool
// Backend, if non-nil, will use this backend for non-enhanced behavior.
// This allows local behavior with remote state storage. It is a way to
// "upgrade" a non-enhanced backend to an enhanced backend with typical
// behavior.
//
// If this is nil, local performs normal state loading and storage.
Backend backend.Backend
// Stateless, if non-nil, puts this backend into the fork's stateless
// mode: the state manager persists nothing and the prior state is a
// projection of the live system. See live.go. Nil - the case for
// every configuration without a "live" block - leaves every code
// path below exactly as it was.
Stateless StatelessRun
// contains filtered or unexported fields
}
Local is an implementation of EnhancedBackend that performs all operations locally. This is the "default" backend and implements normal OpenTofu behavior as it is well known.
func New ¶
func New(enc encryption.StateEncryption) *Local
New returns a new initialized local backend.
func NewWithBackend ¶
func NewWithBackend(backend backend.Backend, enc encryption.StateEncryption) *Local
NewWithBackend returns a new local backend initialized with a dedicated backend for non-enhanced behavior.
func TestLocal ¶
TestLocal returns a configured Local struct with temporary paths and in-memory ContextOpts.
No operations will be called on the returned value, so you can still set public fields without any locks.
func (*Local) ConfigSchema ¶
func (b *Local) ConfigSchema() *configschema.Block
func (*Local) DeleteWorkspace ¶
DeleteWorkspace removes a workspace.
The "default" workspace cannot be removed.
func (*Local) LocalRun ¶
func (b *Local) LocalRun(ctx context.Context, stopCtx context.Context, op *backend.Operation) (*backend.LocalRun, statemgr.Full, tfdiags.Diagnostics)
backend.Local implementation.
func (*Local) Operation ¶
func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.RunningOperation, error)
Operation implements backend.Enhanced
This will initialize an in-memory tofu.Context to perform the operation within this process.
The given operation parameter will be merged with the ContextOpts on the structure with the following rules. If a rule isn't specified and the name conflicts, assume that the field is overwritten if set.
func (*Local) PathsConflictWith ¶
PathsConflictWith returns true if any state path used by a workspace in the receiver is the same as any state path used by the other given local backend instance.
This should be used when "migrating" from one local backend configuration to another in order to avoid deleting the "old" state snapshots if they are in the same files as the "new" state snapshots.
func (*Local) PrepareConfig ¶
func (*Local) ServiceDiscoveryAliases ¶
func (*Local) StatePaths ¶
StatePaths returns the StatePath, StateOutPath, and StateBackupPath as configured from the CLI.
type StateHook ¶
type StateHook struct {
tofu.NilHook
sync.Mutex
StateMgr statemgr.Writer
// If PersistInterval is nonzero then for any new state update after
// the duration has elapsed we'll try to persist a state snapshot
// to the persistent backend too.
// That's only possible if field Schemas is valid, because the
// StateMgr.PersistState function for some backends needs schemas.
PersistInterval time.Duration
// Schemas are the schemas to use when persisting state due to
// PersistInterval. This is ignored if PersistInterval is zero,
// and PersistInterval is ignored if this is nil.
Schemas *tofu.Schemas
// contains filtered or unexported fields
}
StateHook is a hook that continuously updates the state by calling WriteState on a statemgr.Full.
func (*StateHook) PostStateUpdate ¶
type StatelessRun ¶
type StatelessRun interface {
// StateMgr is the state manager for this run. It is called once per
// operation and must return the same object each time, because the
// operation hands it to the state hook and to the final write and expects
// those to be the same manager.
StateMgr() statemgr.Full
// PriorState returns the prior state for the operation: the projection,
// built by reading the live system. It may also modify config, which the
// caller has loaded and not yet used.
//
// Error diagnostics abort the run before anything is planned.
PriorState(ctx context.Context, config *configs.Config, core *tofu.Context) (*states.State, tfdiags.Diagnostics)
// RootOutputData is the live data-source values PriorState read for the
// configuration's root outputs, keyed by absolute resource instance
// address, or nil when it read none - which is the ordinary case.
//
// It is a second return value of PriorState in everything but shape.
// The reason it is a method instead is timing: the values are read while
// the provider instances that serve them are open, inside PriorState,
// and they are USED a moment later by
// [projection.ApplyRootOutputValues], which is the caller's own step
// rather than the run's. Called at most once per operation, always after
// PriorState has returned without errors.
RootOutputData() map[string]cty.Value
// RecordedRootOutputs is what the estate REMEMBERS each root output's
// value to be - the value it settled on at the last apply, or at the
// migration that brought it over from a stock state file - keyed by
// output name, or nil when the run has no record store or nothing is
// recorded.
//
// It is [projection.ApplyRootOutputValues]'s fallback for an output that
// cannot be evaluated against the projection at all, which is what a
// stock state file's own stored output values are to `tofu plan`. A
// method here for [StatelessRun.RootOutputData]'s reason: the store is
// opened inside PriorState and the values are used a moment later, in
// the caller's own step. Called at most once per operation, always after
// PriorState has returned without errors.
RecordedRootOutputs() map[string]cty.Value
// WriteBack is GitHub issue #73's third leg: after a successful apply,
// persist every record-backed resource instance's new state to the
// record store PriorState read from, and delete the record for any
// record-backed instance a destroy removed. finalState is the state the
// apply finished with; schemas is the run's own schema set, already in
// hand at the one call site (opApply).
//
// Called exactly once per apply, after the ordinary state write has
// already succeeded - never mid-apply, and never for a plan-only run.
// A run with no record store configured (or no live block at all) does
// nothing and returns no diagnostics. Error diagnostics here are fatal
// to the run: a version conflict at write-back means this run's apply
// already happened but the record store's record of it may not match
// what actually ran, and that must surface loudly rather than
// silently, the same philosophy live/MARKERS.md's marker-collision
// handling already uses.
WriteBack(ctx context.Context, finalState *states.State, schemas *tofu.Schemas) tfdiags.Diagnostics
// AfterApply runs whatever this run still owes the live system once a
// real apply has finished changing it, and reports what it did as
// diagnostics rather than by returning state: whatever it touches was
// never part of the graph's own state in the first place. A resource
// this run had nothing to do after apply is not an error - AfterApply
// returning no diagnostics at all is the ordinary case, for every run
// with nothing pending and for every plan-only operation, which never
// calls this method to begin with.
AfterApply(ctx context.Context) tfdiags.Diagnostics
}
StatelessRun is the seam stateless mode enters the ordinary local operations through. When Local.Stateless is nil - which is every run of a configuration without a "live" block - nothing in this file is reached and the backend behaves exactly as it always has.
There are three methods because a stateless run has three things to say to the operation, and they happen at different moments:
StateMgr replaces the file-backed state manager, before the operation starts. Everything the operation does to state - the periodic hook, the final write, the emergency persist on interrupt, the lock - goes through that object, so replacing it is what makes "no state was written" a property of the run rather than a list of call sites someone has to remember to skip.
PriorState supplies the prior state, after the configuration is loaded and the OpenTofu context exists but before anything plans. That is the only point where all three things a projection needs are in hand: the configuration to read, a context to get provider schemas from, and a run that has not yet decided anything. It is also where the configuration is rewritten to carry ownership markers, which is why it receives the same *configs.Config the plan is about to read rather than a copy.
AfterApply runs once, only from [Local.opApply] and only after tofu.Context.Apply has returned with no errors - never from [Local.opPlan], and never for a trivial apply that never called Apply at all. It is where a stateless run does whatever a resource that is in the prior state but was never a graph node - never declared in configuration - still needs done to it once real infrastructure has genuinely changed: GitHub issue #67's undeclared_tagged = "untag" verb releases its tag key here, because an orphan with no declared address has no configuration for the ordinary apply graph to hang an update diff off of. See internal/live/untag.
The state manager cannot do the second or third job itself. statemgr.Full has no access to configuration or to configured providers, and both a projection and an apply-time release are built entirely out of those two; a manager that could do either would be a manager that had been handed the whole run.
type TestLocalNoDefaultState ¶
type TestLocalNoDefaultState struct {
*Local
}
TestLocalNoDefaultState is a backend implementation that wraps Local and modifies it to support named states, but not the default state. It returns ErrDefaultWorkspaceNotSupported when the DefaultStateName is used.
func (*TestLocalNoDefaultState) DeleteWorkspace ¶
func (*TestLocalNoDefaultState) Workspaces ¶
func (b *TestLocalNoDefaultState) Workspaces(ctx context.Context) ([]string, error)
type TestLocalSingleState ¶
type TestLocalSingleState struct {
*Local
}
TestLocalSingleState is a backend implementation that wraps Local and modifies it to only support single states (returns ErrWorkspacesNotSupported for multi-state operations).
This isn't an actual use case, this is exported just to provide a easy way to test that behavior.
func (*TestLocalSingleState) DeleteWorkspace ¶
func (*TestLocalSingleState) Workspaces ¶
func (b *TestLocalSingleState) Workspaces(context.Context) ([]string, error)