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
- Variables
- func BindAppGroupScheduledSnapshotContext(ctx context.Context, workspace, operation string) (context.Context, error)
- func BindAppGroupSnapshotChild(ctx context.Context, workspace, parent, child string) (context.Context, error)
- func CommandDeadline(ctx context.Context) (time.Time, bool)
- func CurrentExecutableSHA256() (string, error)
- func ForwardRecoveryPhase(phase string) bool
- func InspectJoin(workspace string, request JoinRequest) error
- func InspectUpgradeTargetApplySnapshot(workspace string, request JoinRequest) (string, error)
- func OpenRecovery(workspace, operationID string) (*Session, Record, error)
- func OpenRestoreActivationRecovery(workspace, operationID string) (*Session, Record, error)
- func OpenUpgradeRecovery(workspace, operationID string) (*Session, Record, error)
- func RecoveryContext(parent context.Context) (context.Context, context.CancelFunc)
- func RequireAppGroupLease(ctx context.Context, workspace, operation string) error
- func RequireIdle(workspace string) error
- func RestoreAppGroupClosuresPending(record Record) bool
- func WithAppGroupCustodyTransaction(ctx context.Context, workspace, operation string, ...) error
- func WithCommandDeadline(ctx context.Context, deadline time.Time) context.Context
- func WithIdleMutation(workspace string, join JoinRequest, execute func() error) error
- func WithIdleMutationContext(ctx context.Context, workspace string, join JoinRequest, ...) error
- func WithSnapshotMutation(workspace string, join JoinRequest, operation string, execute func() error) error
- func WithSnapshotMutationContext(ctx context.Context, workspace string, join JoinRequest, operation string, ...) error
- type BeginRequest
- type CheckpointAuthority
- type JoinAuthority
- type JoinRequest
- type Record
- type ReleaseAuthority
- type RestoreActivationAuthority
- type RestoreActivationBeginRequest
- type RestoreActivationProgress
- type RestoreActivationState
- type RestoreActivationStep
- type RestoreAppGroupClosure
- type Session
- func BeginRestoreActivationPrepared(workspace string, prepare func() (RestoreActivationBeginRequest, error), ...) (*Session, error)
- func BeginRestoreActivationPreparedContext(ctx context.Context, workspace, operation string, ...) (*Session, error)
- func BeginUpgrade(workspace string, request BeginRequest) (*Session, error)
- func BeginUpgradePrepared(workspace string, prepare func() (BeginRequest, error)) (*Session, error)
- func (s *Session) AcknowledgeRestoreAppGroupClose(g backuplifecycle.AppGroupControl) error
- func (session *Session) BeginForwardRecoveryJoin(expected, binaryVersion, executableSHA256 string) (string, error)
- func (session *Session) BeginForwardRetargetJoin(expected string, target ReleaseAuthority) (string, error)
- func (session *Session) BeginJoin(expected, next, command, binaryVersion, executableSHA256 string) (string, error)
- func (session *Session) BeginUpgradeDataActivation(authority UpgradeDataActivationAuthority) error
- func (session *Session) Close() (returnErr error)
- func (session *Session) Complete(status string) error
- func (session *Session) CompletePreparedRelease() error
- func (session *Session) ForwardRecoveryAdmitted() (bool, error)
- func (session *Session) PreparedReleaseAdmitted() (bool, error)
- func (session *Session) ReconcileForRecovery() error
- func (session *Session) Record() Record
- func (s *Session) RecordRestoreAppGroups(groups []backuplifecycle.AppGroupControl) error
- func (s *Session) RecordRestoreControls(controls []backuplifecycle.RuntimeControl) error
- func (session *Session) RequirePreApplyUpgradeRollback() error
- func (s *Session) RequireRestoreAppGroupClosures() error
- func (s *Session) RestoreAppGroupClosed(g backuplifecycle.AppGroupControl) bool
- func (session *Session) Transition(expected, next string) error
- func (session *Session) TransitionRestoreActivation(expectedPhase, nextPhase string, progress RestoreActivationProgress) error
- func (session *Session) TransitionUpgradeDataActivation(expectedPhase, nextPhase string, progress UpgradeDataActivationProgress) error
- func (s *Session) VerifyRestoreAppGroup(g backuplifecycle.AppGroupControl, completed bool) error
- func (s *Session) WithAppGroupContext(ctx context.Context) (context.Context, error)
- type UpgradeDataActivationAuthority
- type UpgradeDataActivationProgress
- type UpgradeDataActivationState
- type UpgradeDataActivationStep
Constants ¶
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" )
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" )
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.
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.
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 ¶
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 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
CommandDeadline reports the deadline recorded by WithCommandDeadline. It survives context.WithoutCancel, unlike the context's own deadline.
func CurrentExecutableSHA256 ¶
CurrentExecutableSHA256 hashes the bytes of the currently executing binary; hidden flags cannot supply or substitute this authority.
func ForwardRecoveryPhase ¶
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 ¶
OpenRecovery acquires the shared lock and accepts only the exact active, signed operation. It never advances or resumes the target path.
func OpenUpgradeRecovery ¶
func RecoveryContext ¶
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 RequireIdle ¶
func RestoreAppGroupClosuresPending ¶ added in v0.54.10
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
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 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 JoinAuthority ¶
type JoinRequest ¶
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
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
ReadRestoreActivationRecord also reads ordinary activation histories without manufacturing app-group membership for unrelated workload verification.
func ReadRestoreAppGroupRecord ¶ added in v0.54.10
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 ¶
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 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 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 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) 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) CompletePreparedRelease ¶
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 ¶
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 ¶
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 ¶
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) 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
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 (*Session) RestoreAppGroupClosed ¶ added in v0.54.10
func (s *Session) RestoreAppGroupClosed(g backuplifecycle.AppGroupControl) bool
func (*Session) Transition ¶
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.
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.