Help us shape the future of Go! Take the 2026 Go Developer Survey

lifecyclemutation

package
v0.55.4 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package lifecyclemutation owns the one local cross-process mutation authority shared by StackSpec authoring, generation, Apply, drift reconcile, and upgrade recovery.

Index

Constants

View Source
const (
	APIVersion = "stackkit.lifecycle-mutation/v1"

	StatusActive    = "active"
	StatusSucceeded = "succeeded"
	StatusRecovered = "recovered"

	KindUpgrade           = "upgrade"
	KindRestoreActivation = "restore-activation"

	PhasePrepared                = "prepared"
	PhaseTargetGenerateStarted   = "target-generate-started"
	PhaseTargetGenerateSucceeded = "target-generate-succeeded"
	PhaseTargetApplyStarted      = "target-apply-started"
	PhaseTargetApplySucceeded    = "target-apply-succeeded"
	PhaseTargetVerifyStarted     = "target-verify-started"
	PhaseTargetVerifySucceeded   = "target-verify-succeeded"
	PhaseCommitStarted           = "commit-started"
	PhaseCommitSucceeded         = "commit-succeeded"
	PhaseRollbackStarted         = "rollback-started"
	PhaseRollbackGenerateStarted = "rollback-generate-started"
	PhaseRollbackGenerateDone    = "rollback-generate-succeeded"
	PhaseRollbackApplyStarted    = "rollback-apply-started"
	PhaseRollbackApplyDone       = "rollback-apply-succeeded"
	PhaseRollbackVerifyStarted   = "rollback-verify-started"
	PhaseRollbackVerifyDone      = "rollback-verify-succeeded"
	PhaseRollbackSucceeded       = "rollback-succeeded"

	PhaseQuiesceStarted                  = "quiesce-started"
	PhaseQuiesced                        = "quiesced"
	PhaseRollbackCopyStarted             = "rollback-copy-started"
	PhaseRollbackCopySucceeded           = "rollback-copy-succeeded"
	PhaseRollbackReady                   = "rollback-ready"
	PhaseActivationCopyStarted           = "activation-copy-started"
	PhaseActivationCopySucceeded         = "activation-copy-succeeded"
	PhaseActivationSucceeded             = "activation-succeeded"
	PhaseRuntimeStartStarted             = "runtime-start-started"
	PhaseRuntimeStartSucceeded           = "runtime-start-succeeded"
	PhaseVerifyStarted                   = "verify-started"
	PhaseVerifySucceeded                 = "verify-succeeded"
	PhaseRollbackVolumeStarted           = "rollback-volume-started"
	PhaseRollbackVolumeSucceeded         = "rollback-volume-succeeded"
	PhaseRollbackRuntimeStarted          = "rollback-runtime-started"
	PhaseRollbackRuntimeSucceeded        = "rollback-runtime-succeeded"
	PhaseRollbackActivationVerifyStarted = "rollback-verify-started"
	PhaseRollbackActivationVerifyDone    = "rollback-verify-succeeded"
)
View Source
const (
	// Upgrade data activation phases are deliberately outside the old
	// generate/apply/verify join phases. A prior executor cannot be admitted
	// while the current controller is copying data back into live volumes.
	PhaseRollbackDataQuiesceStarted          = "rollback-data-quiesce-started"
	PhaseRollbackDataQuiesced                = "rollback-data-quiesced"
	PhaseRollbackDataCopyStarted             = "rollback-data-copy-started"
	PhaseRollbackDataCopySucceeded           = "rollback-data-copy-succeeded"
	PhaseRollbackDataActivationSucceeded     = "rollback-data-activation-succeeded"
	PhaseRollbackPriorRuntimeStartStarted    = "rollback-prior-runtime-start-started"
	PhaseRollbackPriorRuntimeStartSucceeded  = "rollback-prior-runtime-start-succeeded"
	PhaseRollbackPriorRuntimeVerifyStarted   = "rollback-prior-runtime-verify-started"
	PhaseRollbackPriorRuntimeVerifySucceeded = "rollback-prior-runtime-verify-succeeded"
)
View Source
const AuthorityRunningExecutable = "running-executable"

AuthorityRunningExecutable marks a release whose authority is the executable that runs the mutation itself: a same-release target on a host without a workspace release cache. It has no archive digest; the executable digest is its only identity. An empty Authority is a workspace release cache receipt, which always carries its archive digest.

View Source
const JoinClaimCustodyLifetime = "lifetime-lock"

JoinClaimCustodyLifetime marks a claim whose child holds the operation's claim lock until its mutation returns. An older child released that lock right after claiming and wrote no custody, so only this marker lets recovery read a free lock as proof that the claimant ended.

View Source
const RecoveryBudget = 10 * time.Minute

RecoveryBudget is the longest a compensating operation may run. A mutation that must leave room for its own recovery reserves this much of the command deadline.

Variables

View Source
var ErrUpgradeDataActivationRequired = errors.New("lifecycle mutation: prior-runtime rollback requires verified live data activation after target Apply or an ambiguous recovery phase; after target Apply, stackkit upgrade --recover <operation-id> --forward completes the target release instead")

ErrUpgradeDataActivationRequired prevents a prior runtime from opening data that a newer runtime may already have changed. Isolated backup staging alone is not proof that the live data belongs to the prior runtime.

Functions

func BindAppGroupScheduledSnapshotContext added in v0.54.10

func BindAppGroupScheduledSnapshotContext(ctx context.Context, workspace, operation string) (context.Context, error)

func BindAppGroupSnapshotChild added in v0.54.10

func BindAppGroupSnapshotChild(ctx context.Context, workspace, parent, child string) (context.Context, error)

BindAppGroupSnapshotChild delegates the held preparation lease to one named child snapshot. Its own signed backup journal remains its actuation authority.

func CommandDeadline added in v0.50.1

func CommandDeadline(ctx context.Context) (time.Time, bool)

CommandDeadline reports the deadline recorded by WithCommandDeadline. It survives context.WithoutCancel, unlike the context's own deadline.

func CurrentExecutableSHA256

func CurrentExecutableSHA256() (string, error)

CurrentExecutableSHA256 hashes the bytes of the currently executing binary; hidden flags cannot supply or substitute this authority.

func ForwardRecoveryPhase

func ForwardRecoveryPhase(phase string) bool

ForwardRecoveryPhase reports the phases after target Apply was admitted from which an owner may complete the target release forward instead of rolling it back.

func InspectJoin

func InspectJoin(workspace string, request JoinRequest) error

InspectJoin validates a signed child admission without consuming its one-use nonce. It is safe for pre-observability admission only.

func InspectUpgradeTargetApplySnapshot

func InspectUpgradeTargetApplySnapshot(workspace string, request JoinRequest) (string, error)

InspectUpgradeTargetApplySnapshot exposes the signed pre-target checkpoint only to the exact admitted upgrade Apply child. The caller can use its original Compose definition to prove ownership of a running old listener after target Generate has replaced the active Compose file.

func OpenRecovery

func OpenRecovery(workspace, operationID string) (*Session, Record, error)

OpenRecovery acquires the shared lock and accepts only the exact active, signed operation. It never advances or resumes the target path.

func OpenRestoreActivationRecovery

func OpenRestoreActivationRecovery(
	workspace, operationID string,
) (*Session, Record, error)

func OpenUpgradeRecovery

func OpenUpgradeRecovery(workspace, operationID string) (*Session, Record, error)

func RecoveryContext

func RecoveryContext(parent context.Context) (context.Context, context.CancelFunc)

RecoveryContext gives an already authorized compensating operation its own bounded phase. Cancellation of the failed mutation must not also cancel its recovery, but the recovery still ends by the command deadline so it reports its outcome instead of being killed mid-way. Callers must retain the mutation lock and verify recovery authority.

func RequireAppGroupLease added in v0.54.10

func RequireAppGroupLease(ctx context.Context, workspace, operation string) error

func RequireIdle

func RequireIdle(workspace string) error

func RestoreAppGroupClosuresPending added in v0.54.10

func RestoreAppGroupClosuresPending(record Record) bool

func WithAppGroupCustodyTransaction added in v0.54.10

func WithAppGroupCustodyTransaction(ctx context.Context, workspace, operation string, execute func(*confinedfs.Transaction, confinedfs.View) error) error

WithAppGroupCustodyTransaction authorizes native generation CAS at the same owning lifecycle lock. Borrow pins both exact transaction and held OS lock through callback completion, including bounded detached cleanup. This is no ambient/global lock lookup and a released context can never reacquire it.

func WithCommandDeadline added in v0.50.1

func WithCommandDeadline(ctx context.Context, deadline time.Time) context.Context

WithCommandDeadline records the time by which the whole command, recovery included, must have returned. Its caller (for example Techstack's agent) kills the process at that point, so a recovery that runs past it is lost.

func WithIdleMutation

func WithIdleMutation(
	workspace string,
	join JoinRequest,
	execute func() error,
) error

WithIdleMutation holds the same lock as an upgrade Session across the exact ordinary mutation boundary. A joined upgrade child is admitted from the signed parent journal and deliberately does not reacquire the parent's lock.

func WithIdleMutationContext added in v0.54.10

func WithIdleMutationContext(ctx context.Context, workspace string, join JoinRequest, execute func(context.Context) error) error

func WithSnapshotMutation added in v0.53.13

func WithSnapshotMutation(workspace string, join JoinRequest, operation string, execute func() error) error

WithSnapshotMutation admits recovery of only the original authenticated snapshot operation. Its service still revalidates exact Plan/Owner input; a changed operation or Plan cannot discard an older control obligation.

func WithSnapshotMutationContext added in v0.54.10

func WithSnapshotMutationContext(ctx context.Context, workspace string, join JoinRequest, operation string, execute func(context.Context) error) error

WithSnapshotMutationContext propagates the actual existing OS lease. It replaces, rather than nests inside, WithSnapshotMutation at group call sites. Joined legacy children retain their old admission but get no fabricated lease.

Types

type BeginRequest

type BeginRequest struct {
	OperationID string
	OwnerRef    string
	Checkpoint  CheckpointAuthority
	Target      ReleaseAuthority
	Prior       ReleaseAuthority
}

type CheckpointAuthority

type CheckpointAuthority struct {
	ExecutorStateSnapshotID string `json:"executorStateSnapshotId"`
	KopiaAnchorID           string `json:"kopiaAnchorId"`
}

type JoinAuthority

type JoinAuthority struct {
	Command          string `json:"command"`
	BinaryVersion    string `json:"binaryVersion"`
	ExecutableSHA256 string `json:"executableSha256"`
	NonceSHA256      string `json:"nonceSha256"`
}

type JoinRequest

type JoinRequest struct {
	OperationID      string
	Phase            string
	BinaryVersion    string
	ExecutableSHA256 string
	Nonce            string
	Command          string
}

type Record

type Record struct {
	APIVersion            string                                        `json:"apiVersion"`
	OperationID           string                                        `json:"operationId"`
	Kind                  string                                        `json:"kind"`
	WorkspaceHash         string                                        `json:"workspaceHash"`
	OwnerRef              string                                        `json:"ownerRef"`
	Status                string                                        `json:"status"`
	Phase                 string                                        `json:"phase"`
	Sequence              uint64                                        `json:"sequence"`
	PreviousRecordDigest  string                                        `json:"previousRecordDigest,omitempty"`
	Checkpoint            CheckpointAuthority                           `json:"checkpoint"`
	Target                ReleaseAuthority                              `json:"target"`
	Prior                 ReleaseAuthority                              `json:"prior"`
	RestoreActivation     *RestoreActivationState                       `json:"restoreActivation,omitempty"`
	UpgradeDataActivation *UpgradeDataActivationState                   `json:"upgradeDataActivation,omitempty"`
	Join                  *JoinAuthority                                `json:"join,omitempty"`
	UpdatedAt             time.Time                                     `json:"updatedAt"`
	Signature             localevidence.OwnerLifecycleMutationSignature `json:"signature"`
}

func ActiveRecord added in v0.51.3

func ActiveRecord(workspace string) (Record, bool, error)

ActiveRecord returns the verified record of the active lifecycle mutation or terminal restore with pending native closes, without taking the lock. It only inspects: acting on the mutation still needs OpenRecovery, which revalidates the record under the lock.

func ReadRestoreActivationRecord added in v0.54.10

func ReadRestoreActivationRecord(workspace, operation string) (Record, error)

ReadRestoreActivationRecord also reads ordinary activation histories without manufacturing app-group membership for unrelated workload verification.

func ReadRestoreAppGroupRecord added in v0.54.10

func ReadRestoreAppGroupRecord(workspace, operation string) (Record, error)

ReadRestoreAppGroupRecord reads existing immutable signed history without acquiring a mutation Session, repairing a pointer or writing recovery state. It is intended for real-product journey assertions and diagnostic readback.

func (Record) RequirePreApplyUpgradeRollback

func (record Record) RequirePreApplyUpgradeRollback() error

RequirePreApplyUpgradeRollback admits executor-only recovery while the signed journal proves that no target Apply was admitted. Once Apply may have run, recovery must restore the complete prior data set before starting old images. A bare rollback-started record does not retain that proof and is denied too.

type ReleaseAuthority

type ReleaseAuthority struct {
	Authority        string `json:"authority,omitempty"`
	Version          string `json:"version"`
	ArchiveSHA256    string `json:"archiveSha256"`
	ExecutableSHA256 string `json:"executableSha256"`
}

type RestoreActivationAuthority

type RestoreActivationAuthority struct {
	OperationID          string   `json:"operationId"`
	OwnerRef             string   `json:"ownerRef"`
	RestoreResultID      string   `json:"restoreResultId"`
	SafetySnapshotID     string   `json:"safetySnapshotId"`
	PlanHash             string   `json:"planHash"`
	ManifestHash         string   `json:"manifestHash"`
	ApplyResultHash      string   `json:"applyResultHash"`
	ManagedVolumeSetHash string   `json:"managedVolumeSetHash"`
	Volumes              []string `json:"volumes"`
}

type RestoreActivationBeginRequest

type RestoreActivationBeginRequest struct {
	OperationID          string
	OwnerRef             string
	RestoreResultID      string
	SafetySnapshotID     string
	PlanHash             string
	ManifestHash         string
	ApplyResultHash      string
	ManagedVolumeSetHash string
	Volumes              []string
}

type RestoreActivationProgress

type RestoreActivationProgress struct {
	RollbackPrepared []string               `json:"rollbackPrepared"`
	Activated        []string               `json:"activated"`
	InFlight         *RestoreActivationStep `json:"inFlight,omitempty"`
}

type RestoreActivationState

type RestoreActivationState struct {
	AppGroupClosures []RestoreAppGroupClosure          `json:"appGroupClosures,omitempty"`
	AppGroups        []backuplifecycle.AppGroupControl `json:"appGroups,omitempty"`
	Controls         []backuplifecycle.RuntimeControl  `json:"controls,omitempty"`
	Authority        RestoreActivationAuthority        `json:"authority"`
	RollbackPrepared []string                          `json:"rollbackPrepared"`
	Activated        []string                          `json:"activated"`
	InFlight         *RestoreActivationStep            `json:"inFlight,omitempty"`
}

type RestoreActivationStep

type RestoreActivationStep struct {
	Volume string `json:"volume"`
}

type RestoreAppGroupClosure added in v0.54.10

type RestoreAppGroupClosure struct {
	GroupID          string `json:"groupId"`
	CompletionDigest string `json:"completionDigest"`
}

CompletionDigest is exactly the original native close request's group digest. ACKs are siblings of AppGroups, so recording one cannot change that digest or require a different native close on recovery. Absent v1 fields remain absent in canonical signing bytes.

type Session

type Session struct {
	// contains filtered or unexported fields
}

func BeginRestoreActivationPrepared

func BeginRestoreActivationPrepared(
	workspace string,
	prepare func() (RestoreActivationBeginRequest, error),
	onAbort ...func() error,
) (*Session, error)

BeginRestoreActivationPrepared holds the shared lifecycle lock while the caller prepares the immutable restore and safety-snapshot authority.

func BeginRestoreActivationPreparedContext added in v0.54.10

func BeginRestoreActivationPreparedContext(ctx context.Context, workspace, operation string, prepare func(context.Context) (RestoreActivationBeginRequest, error), onAbort ...func() error) (*Session, error)

func BeginUpgrade

func BeginUpgrade(workspace string, request BeginRequest) (*Session, error)

func BeginUpgradePrepared

func BeginUpgradePrepared(
	workspace string,
	prepare func() (BeginRequest, error),
) (*Session, error)

BeginUpgradePrepared holds the shared lifecycle lock while the caller prepares the exact checkpoint/release authority and persists the first signed journal record without a checkpoint-to-Begin race.

func (*Session) AcknowledgeRestoreAppGroupClose added in v0.54.10

func (s *Session) AcknowledgeRestoreAppGroupClose(g backuplifecycle.AppGroupControl) error

AcknowledgeRestoreAppGroupClose is ONLY called after original native close ACK/readback. It appends a signed phase at the same terminal status under the existing Session/OS lease. Lost native or journal replies replay the identical group completion digest; no replacement operation is admitted.

func (*Session) BeginForwardRecoveryJoin

func (session *Session) BeginForwardRecoveryJoin(
	expected, binaryVersion, executableSHA256 string,
) (string, error)

BeginForwardRecoveryJoin re-admits the exact signed target Apply of an upgrade interrupted after target Apply was admitted. Rerunning the target Apply is the documented resume of a killed Apply, and it never starts the prior runtime on data the target may already have changed. It requires explicit recovery custody: the shared lifecycle lock and the join-claim lock, with no live claimant of the current phase. The new signed phase invalidates the abandoned child's nonce before the claim lock is handed to the re-admitted child.

func (*Session) BeginForwardRetargetJoin

func (session *Session) BeginForwardRetargetJoin(
	expected string,
	target ReleaseAuthority,
) (string, error)

BeginForwardRetargetJoin admits a target Generate for forward completion of an upgrade whose history admitted target Apply. With a newer verified release it supersedes the signed target: the owner approves that when the signed target itself cannot finish the operation, for example because its own Apply is defective. With the signed target itself it regenerates after a failed retarget. The checkpoint and prior release stay the recovery authority. Only the signed or a strictly newer release from the verified workspace release cache is accepted, so live data the interrupted Apply may have changed is never opened by an older runtime. Operations whose signed target is the running executable (change sets, coordinated rollbacks) are refused. Custody requirements equal BeginForwardRecoveryJoin's.

func (*Session) BeginJoin

func (session *Session) BeginJoin(
	expected, next, command, binaryVersion, executableSHA256 string,
) (string, error)

func (*Session) BeginUpgradeDataActivation

func (session *Session) BeginUpgradeDataActivation(
	authority UpgradeDataActivationAuthority,
) error

BeginUpgradeDataActivation records the source/target custody binding and enters the quiescence phase under the already-held KindUpgrade lock.

func (*Session) Close

func (session *Session) Close() (returnErr error)

func (*Session) Complete

func (session *Session) Complete(status string) error

func (*Session) CompletePreparedRelease

func (session *Session) CompletePreparedRelease() error

CompletePreparedRelease completes an upgrade that never left its prepared phase as recovered, without any rollback phase. It re-proves the exact prepared history under the held lifecycle lock before signing.

func (*Session) ForwardRecoveryAdmitted

func (session *Session) ForwardRecoveryAdmitted() (bool, error)

ForwardRecoveryAdmitted reports whether forward completion may resume from the current phase: after target Apply admission, or at the target Generate that a forward retarget signed after target Apply had been admitted.

func (*Session) PreparedReleaseAdmitted

func (session *Session) PreparedReleaseAdmitted() (bool, error)

PreparedReleaseAdmitted reports whether the operation is an upgrade whose complete signed history never left the prepared phase: no target or rollback phase, no child join and no data activation was ever recorded, so nothing of the target ran and no prior release effect needs to be replayed.

func (*Session) ReconcileForRecovery

func (session *Session) ReconcileForRecovery() error

ReconcileForRecovery repairs a stale active pointer from the immutable signed history while the caller still holds the lifecycle lock. A failed transition can therefore roll back in the same process when its history record was installed before the pointer write reported an error. Root and identity failures remain fail-closed in recoverLatestRecord.

func (*Session) Record

func (session *Session) Record() Record

func (*Session) RecordRestoreAppGroups added in v0.54.10

func (s *Session) RecordRestoreAppGroups(groups []backuplifecycle.AppGroupControl) error

RecordRestoreAppGroups uses the same signed Session CAS and phase authority as existing restore copies. Empty fields preserve original v1 wire bytes.

func (*Session) RecordRestoreControls added in v0.53.13

func (s *Session) RecordRestoreControls(controls []backuplifecycle.RuntimeControl) error

RecordRestoreControls writes per-effect progress into the current signed activation phase, with the same lock, CAS and journal custody as copy steps.

func (*Session) RequirePreApplyUpgradeRollback added in v0.53.4

func (session *Session) RequirePreApplyUpgradeRollback() error

RequirePreApplyUpgradeRollback can resume unclaimed or completed prior Generate while explicit recovery owns the claim race and the complete signed history proves target Apply was never admitted. A Record alone cannot prove this: a forward retarget signs target Generate again after target Apply was admitted, so even a target Generate phase is checked against the history.

func (*Session) RequireRestoreAppGroupClosures added in v0.54.10

func (s *Session) RequireRestoreAppGroupClosures() error

func (*Session) RestoreAppGroupClosed added in v0.54.10

func (s *Session) RestoreAppGroupClosed(g backuplifecycle.AppGroupControl) bool

func (*Session) Transition

func (session *Session) Transition(expected, next string) error

func (*Session) TransitionRestoreActivation

func (session *Session) TransitionRestoreActivation(
	expectedPhase, nextPhase string,
	progress RestoreActivationProgress,
) error

func (*Session) TransitionUpgradeDataActivation

func (session *Session) TransitionUpgradeDataActivation(
	expectedPhase, nextPhase string,
	progress UpgradeDataActivationProgress,
) error

TransitionUpgradeDataActivation persists one exact, resumable phase under the same signed KindUpgrade journal. It never creates a second journal or a child-executor join authority.

func (*Session) VerifyRestoreAppGroup added in v0.54.10

func (s *Session) VerifyRestoreAppGroup(g backuplifecycle.AppGroupControl, completed bool) error

VerifyRestoreAppGroup checks the actual signed Session CAS/readback. A native adapter cannot grant itself journal authority by repeating a binding digest.

func (*Session) WithAppGroupContext added in v0.54.10

func (s *Session) WithAppGroupContext(ctx context.Context) (context.Context, error)

WithAppGroupContext binds this live restore Session without minting a token or acquiring another lock. Every consumer re-borrows/verifies the OS lease.

type UpgradeDataActivationAuthority

type UpgradeDataActivationAuthority struct {
	SourceGraphDigest    string   `json:"sourceGraphDigest"`
	TargetTopologyDigest string   `json:"targetTopologyDigest"`
	TargetCustodyDigest  string   `json:"targetCustodyDigest"`
	RestoreResultID      string   `json:"restoreResultId"`
	ManagedVolumeSetHash string   `json:"managedVolumeSetHash"`
	LiveVolumes          []string `json:"liveVolumes"`
}

UpgradeDataActivationAuthority is the immutable, owner-signed binding for post-Apply prior-data activation. The lifecycle record already carries the operation owner and release/checkpoint authority, so they are intentionally not duplicated here.

type UpgradeDataActivationProgress

type UpgradeDataActivationProgress struct {
	CompletedPrefix []string                   `json:"completedPrefix"`
	InFlight        *UpgradeDataActivationStep `json:"inFlight,omitempty"`
}

UpgradeDataActivationProgress is the caller's next signed progress value. CompletedPrefix is the exact ordered prefix whose copies completed; InFlight is persisted before a copy starts and remains until its completion transition is committed.

type UpgradeDataActivationState

type UpgradeDataActivationState struct {
	Authority       UpgradeDataActivationAuthority `json:"authority"`
	CompletedPrefix []string                       `json:"completedPrefix"`
	InFlight        *UpgradeDataActivationStep     `json:"inFlight,omitempty"`
}

UpgradeDataActivationState is stored additively on a KindUpgrade record only once post-Apply data recovery actually begins. Keeping it nil before then preserves the old record shape for older controllers.

type UpgradeDataActivationStep

type UpgradeDataActivationStep struct {
	Volume string `json:"volume"`
}

UpgradeDataActivationStep identifies the one volume whose copy may have started before a process crash. Retrying that step is deliberately allowed.

Jump to

Keyboard shortcuts

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