statemove

package
v0.41.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package statemove validates declared Terraform resource moves against plan data and generates/maintains the native `moved {}` shim files that effect them (SP1: within one stack). It consumes tfplan.json — it does not run terraform.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckXMoveSource added in v0.18.6

func CheckXMoveSource(src *tfjson.Plan, from string) error

CheckXMoveSource verifies that src has a prior_state containing at least one resource under from. Returns an error when prior_state is absent (source stack has no state — nothing to move) or when nothing under from is found (wrong address prefix or wrong stack).

func Cleanup

func Cleanup(root, key string) (int, error)

Cleanup removes same-state shim files (_tfsp_move.*.tf) matching key; an empty key removes ALL shims. It matches by FILENAME and does not parse, so a corrupt or key-mismatched shim (which Discover rejects) is still removable. Returns the number of files removed.

func CleanupXMoves added in v0.9.7

func CleanupXMoves(root, key string) (int, error)

CleanupXMoves removes cross-state xmove manifests (_tfsp_xmove.*.hcl) matching key; an empty key removes ALL. Like Cleanup it matches by filename and does not parse. Returns the number removed.

func DataSourceOrphans added in v0.19.0

func DataSourceOrphans(pairs []Move, dataSources []string) []string

DataSourceOrphans returns the data-source addresses from dataSources that fall under any declared pair's From prefix. These remain in the source stack after the move because stateAddresses filters data sources out of the move set — the operator may need to forget them (terraform state rm) before the source stack can be retired.

func IsSpent added in v0.19.0

func IsSpent(pairs []Move, dstPriorAddrs AddressSet) bool

IsSpent reports whether all declared moves have already been applied: every pair's To-prefix has at least one address present in dstPriorAddrs. Returns false when dstPriorAddrs is empty (indeterminate) or pairs is empty.

func PriorStateDataSources added in v0.19.0

func PriorStateDataSources(planJSON []byte) []string

PriorStateDataSources parses prior_state from a raw Terraform plan JSON and returns the addresses of data sources (Mode==data). These are excluded from stateAddresses but may be stranded in the source stack after a move if they fall under the declared From prefix — use DataSourceOrphans to find them. Returns nil when there is no prior_state or the JSON is unparseable.

func RenderShim

func RenderShim(key string, ops []Op) string

RenderShim renders a PR-keyed shim: a machine-readable header (banner, key, one tab-separated op line each) followed by the native Terraform blocks. The header is the source of truth for list/maintenance.

func RenderXMove

func RenderXMove(key string, xm XMove) string

RenderXMove renders the HCL manifest (key in a header comment + an xmove block).

func ShimFileName

func ShimFileName(key string) string

ShimFileName is the colocated shim filename for a key, e.g. "_tfsp_move.PR-123.tf".

func ValidateMove

func ValidateMove(plan *tfjson.Plan, from, to string) error

ValidateMove fail-closes unless the plan shows a clean relocation from→to: every resource being destroyed under `from` has a created counterpart under `to` at the rewritten address with the same type, and vice versa (no unmatched creates under `to`). Works for a single resource (exact match), a whole module (prefix), and count/for_each (bracketed) forms.

func XMoveFileName

func XMoveFileName(key string) string

XMoveFileName is the manifest filename for a key.

Types

type Action

type Action struct {
	From, To string
	Decision Decision
}

Action is what Execute did/would do for one pair.

func Execute

func Execute(ctx context.Context, deps ExecDeps, root, destStack string, xm XMove, dryRun bool) ([]Action, error)

Execute performs one xmove (dest = root/destStack, source = root/xm.SourceStack): pull both states → backup → decide per pair → (unless dryRun) state mv the to-move pairs on local files and push both (never --force). Fail-closed: any error aborts before pushing.

type AddressSet added in v0.18.2

type AddressSet map[string]string

AddressSet maps resource addresses to their corresponding ProviderName.

func PriorStateAddrs added in v0.18.5

func PriorStateAddrs(planJSON []byte) AddressSet

PriorStateAddrs parses prior_state from a raw Terraform plan JSON document and returns an AddressSet mapping every resource address to its provider name. Returns nil when the JSON is unparseable or the plan has no prior_state.

The prior_state is the raw Terraform state before the plan ran — the same addresses that apply-time xmove validation will see in the live state. Using this instead of ResourceChanges for source address validation prevents module-level moved{} blocks from causing a plan/apply split: those blocks rename resources inside ResourceChanges (e.g. module.foo → module.foo[0]) without setting per-resource PreviousAddress, so the plan shows post-rename addresses while the live state still holds the originals.

Parsing bypasses tfjson.Plan/State.Validate() (which requires FormatVersion) so that this function works on both real plan JSON and test fixtures.

type Decision

type Decision int

Decision is the per-move runtime action derived from the two live states.

const (
	DecisionMove Decision = iota // source has it, dest doesn't → move
	DecisionSkip                 // dest already has it → idempotent skip
)

type DestProviders added in v0.18.2

type DestProviders map[string]bool

DestProviders represents a set of destination providers.

func DiscoverDestProviders added in v0.18.2

func DiscoverDestProviders(stackDir string) DestProviders

DiscoverDestProviders scans a stack directory for defined providers in HCL config.

type Diagnostic added in v0.18.2

type Diagnostic struct {
	Code     string
	Severity Severity
	Message  string
}

func ValidateMovePlan added in v0.18.2

func ValidateMovePlan(src, dst, dstPrior AddressSet, providers DestProviders, m XMove, isApply bool) []Diagnostic

ValidateMovePlan validates a cross-state move manifest against source and destination AddressSets and configured providers.

dstPrior is the destination stack's prior_state address set, used only at plan time (isApply=false) to tell a spent manifest entry from a stale one: a source address absent from the source plan whose To-address is already in dstPrior was moved by an earlier apply — warning (xmove/spent), not error. Apply-time callers validate against live states (dst *is* the prior state) and may pass nil.

type ExecDeps

type ExecDeps struct {
	NewTF     func(workingDir string) (Runner, error) // builds a Runner rooted at a stack dir
	BackupDir string                                  // pre-move state backups (empty disables)
	Locker    Locker                                  // optional pessimistic lock around the move (nil = none)
}

ExecDeps injects the terraform factory + a backup dir.

type FoundXMove

type FoundXMove struct {
	Path      string
	DestStack string // dir relative to root (slash form)
	Key       string
	XMove     XMove
}

FoundXMove is a discovered manifest with its location.

func DiscoverXMoves

func DiscoverXMoves(root string) ([]FoundXMove, error)

DiscoverXMoves walks root for `_tfsp_xmove.*.hcl` manifests. It is fail-closed: a file in our reserved namespace that won't parse, or whose header key disagrees with its filename, returns an error (the filename is authoritative). Use CleanupXMoves to remove such a file.

type Locker

type Locker interface {
	Acquire(ctx context.Context, stackDir string) (release func() error, err error)
}

Locker is an optional pessimistic lock acquired before the pull→mv→push window so a concurrent terraform op on the same state fails to lock (rather than the move failing mid-flight). Acquire returns a release func; an error means the state is already locked and the move must NOT proceed.

type Move

type Move struct {
	From string
	To   string
}

Move is one declared relocation, from one address to another (same stack in SP1). Addresses are opaque Terraform addresses (resource, module, or with count/for_each brackets).

func CrossStackPairs

func CrossStackPairs(src, dst *tfjson.Plan, from, to string) ([]Move, error)

CrossStackPairs validates a cross-stack move and returns the matched from→to address pairs (fail-closed; same rules as ClassifyCrossStack minus the import id). Used by the `state mv` path, which is address-based and needs no id.

type Op

type Op struct {
	Kind string
	From string
	To   string
	ID   string
}

Op is one operation in a shim file. Kind is "moved" (From→To, same state), "import" (To + ID, adopt into this state), or "removed" (From, drop from this state without destroying). Addresses/ids never contain a tab, so the header encodes ops tab-separated.

func ClassifyCrossStack

func ClassifyCrossStack(src, dst *tfjson.Plan, from, to string) (srcOps, dstOps []Op, err error)

ClassifyCrossStack validates a cross-stack move from `from` (in src's plan) to `to` (in dst's plan) and returns the ops to write: `removed` ops in the source shim and `import` ops (id from src's before.id) in the destination shim. Fail- closed: every resource destroyed under `from` must be created under `to` at the rewritten address with the same type and a usable import id, and every create under `to` must have a destroyed counterpart.

func MergeOps

func MergeOps(existing, add []Op) []Op

MergeOps appends add to existing, de-duplicating whole ops, preserving order.

func ParseShim

func ParseShim(content string) (key string, ops []Op, err error)

ParseShim reads a shim's header back into key + ops. Errors if not a tfstackplan shim.

type Runner

type Runner interface {
	Init(ctx context.Context, opts ...tfexec.InitOption) error
	StatePull(ctx context.Context, opts ...tfexec.StatePullOption) (string, error)
	StatePush(ctx context.Context, path string, opts ...tfexec.StatePushCmdOption) error
	ShowStateFile(ctx context.Context, statePath string, opts ...tfexec.ShowOption) (*tfjson.State, error)
	StateMv(ctx context.Context, source, destination string, opts ...tfexec.StateMvCmdOption) error
}

Runner is the subset of *tfexec.Terraform the executor needs (so tests can fake it without a binary).

func NewTerraform

func NewTerraform(execPath, workingDir string) (Runner, error)

NewTerraform builds the real Runner for a stack dir.

type Severity added in v0.18.2

type Severity string
const (
	SeverityError   Severity = "error"
	SeverityWarning Severity = "warning"
)

type Shim

type Shim struct {
	Path  string // path to the .tf
	Stack string // dir containing it, relative to the discover root (slash form)
	Key   string
	Ops   []Op
}

Shim is a discovered shim file.

func Discover

func Discover(root string) ([]Shim, error)

Discover walks root for `_tfsp_move.*.tf` shim files and parses each. It is fail-closed: a file in our reserved namespace that won't parse, or whose header key disagrees with its filename, returns an error (the filename is the authoritative key). Use Cleanup to remove such a file.

type XMove

type XMove struct {
	SourceStack string
	Pairs       []Move
}

XMove is a cross-state move manifest, colocated in the DESTINATION stack: pull the Pairs' source addresses out of SourceStack's state and into this stack's.

func ParseXMove

func ParseXMove(content string) (key string, xm XMove, err error)

ParseXMove parses the manifest content into its key + XMove.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL