Documentation
¶
Overview ¶
Package update implements the staged core update lifecycle: a persisted operation whose phases (compatibility, release verification, backup, activation, health and links, final acceptance) are reported and completed independently, protected by a durable lease, driven by a resumable job, and reconciled after a process restart. Sending a deployment request never counts as a successful update: activation needs the serving process to report the expected identity, and acceptance needs every required check to pass.
Failure handling may activate a compatible prior artifact on the current data. It never restores a database or media snapshot over newer writes.
Index ¶
- Constants
- Variables
- func DecodeArtifact(raw json.RawMessage, a *Artifact) bool
- func IsRelease(s string) bool
- func Migrations() fs.FS
- func NewOwnerToken() string
- func Newer(candidate, current string) bool
- type Activation
- type Adapter
- type Artifact
- type Config
- type CoolifyAdapter
- type ErrConflict
- type ErrLeaseHeld
- type Holder
- type Lease
- func (l *Lease) Acquire(ctx context.Context, holder, owner, operationID string) error
- func (l *Lease) AcquireTx(ctx context.Context, tx *sql.Tx, holder, owner, operationID string) error
- func (l *Lease) Current(ctx context.Context) (Holder, bool, error)
- func (l *Lease) Release(ctx context.Context, owner string) error
- func (l *Lease) Renew(ctx context.Context, owner string) error
- type ManualAdapter
- type Operation
- type Phase
- type ReleaseIndex
- type Report
- type Request
- type Service
- func (s *Service) Accepted(ctx context.Context) (string, error)
- func (s *Service) Active(ctx context.Context) (*Operation, error)
- func (s *Service) AdapterName() string
- func (s *Service) Configured() bool
- func (s *Service) Get(ctx context.Context, id string) (Operation, error)
- func (s *Service) Last(ctx context.Context) (*Operation, error)
- func (s *Service) Lease() *Lease
- func (s *Service) List(ctx context.Context, limit int) ([]Operation, error)
- func (s *Service) Reconcile(ctx context.Context, atBoot bool) error
- func (s *Service) Register()
- func (s *Service) Request(ctx context.Context, req Request) (Operation, error)
- func (s *Service) RunningVersion() string
- func (s *Service) Source() Source
- func (s *Service) SourceName() string
- type Source
- type TargetVerifier
Constants ¶
const ( ActivationQueued = "queued" ActivationRunning = "running" ActivationActive = "active" ActivationFailed = "failed" ActivationUnknown = "unknown" )
Activation statuses an adapter reports for a deployment reference.
const ( HolderUpdate = "update" HolderBackup = "backup" HolderRestore = "restore" )
Lease holders.
const ( PhaseCompatibility = "compatibility" PhaseVerification = "verification" PhaseBackup = "backup" PhaseActivation = "activation" PhaseHealth = "health" PhaseAcceptance = "acceptance" PhaseRollbackCompatibility = "rollback_compatibility" PhaseRollbackActivation = "rollback_activation" PhaseRollbackHealth = "rollback_health" )
Phase names, in lifecycle order; rollback phases are appended on demand.
const ( StatusPending = "pending" StatusRunning = "running" StatusSucceeded = "succeeded" StatusFailed = "failed" StatusInterrupted = "interrupted" StatusRolledBack = "rolled_back" StatusRollbackFailed = "rollback_failed" )
Operation statuses.
const ( PhasePending = "pending" PhaseRunning = "running" PhasePassed = "passed" PhaseFailed = "failed" PhaseSkipped = "skipped" PhaseInterrupted = "interrupted" )
Phase statuses.
const ( MigrationUnchecked = "unchecked" MigrationUnchanged = "unchanged" MigrationApplied = "applied" MigrationUnknown = "unknown" )
Migration states recorded on an operation.
const JobKind = "update.run"
JobKind runs one operation; it is resumable and never retried blindly.
const LeaseScope = "data"
LeaseScope is the one scope every data-directory operation competes for.
const ServiceName = "update"
ServiceName is the kernel service name the admin module publishes.
Variables ¶
var ( ErrInvalidTarget = errors.New("update: target is not a release version") ErrNotFound = errors.New("update: operation not found") )
Errors returned by Request.
var ErrLeaseLost = errors.New("update: lease lost")
ErrLeaseLost is returned by Renew when the caller no longer owns the lease.
var ErrNotConfigured = errors.New("update: release source not configured")
ErrNotConfigured means no trusted release source is configured.
var ErrUnpinnable = errors.New("update: adapter cannot pin the requested artifact")
ErrUnpinnable means the adapter cannot guarantee that activation targets the requested artifact (for example a Coolify resource that builds from a git branch). The lifecycle refuses such an update instead of presenting a branch deployment as a version-directed update.
Functions ¶
func DecodeArtifact ¶
func DecodeArtifact(raw json.RawMessage, a *Artifact) bool
DecodeArtifact reads a saved observation payload into a.
Types ¶
type Activation ¶
type Activation struct {
Status string `json:"status" enum:"queued,running,active,failed,unknown"`
Detail string `json:"detail"`
}
Activation is the provider's view of a deployment reference. Active means the provider says the artifact runs; the lifecycle still requires the serving process to report the expected identity before the phase passes.
type Adapter ¶
type Adapter interface {
Name() string
// Activate asks the provider to make artifact active and returns a
// durable reference. Acceptance is not activation.
Activate(ctx context.Context, artifact Artifact) (string, error)
// Status reports the state of a reference returned by Activate.
Status(ctx context.Context, ref string) (Activation, error)
}
Adapter requests activation of an artifact through a local supervisor or a configured deployment provider. Credentials and hosts come from server configuration only.
type Artifact ¶
type Artifact struct {
Version string `json:"version"`
ID string `json:"id" doc:"Immutable artifact identity, e.g. an image reference or archive name"`
Digest string `json:"digest" doc:"sha256:… from the release source; empty when the source publishes none"`
Source string `json:"source"`
// Migrations and Themes are compatibility data the release source may
// publish; nil means the source did not say.
Migrations []string `json:"migrations,omitempty"`
Themes []string `json:"themes,omitempty"`
}
Artifact is an immutable release identity from a trusted source.
type Config ¶
type Config struct {
// RunningVersion is the serving process identity (the build string),
// never a selected artifact or saved target.
RunningVersion string
Source Source
Adapter Adapter
// AllowDowngrade lets a lower target pass compatibility when the
// operator accepts the recorded downgrade constraints.
AllowDowngrade bool
// RequireDigest fails verification when the source publishes no digest.
RequireDigest bool
// ActivationTimeout bounds waiting for a provider or operator to
// activate the artifact (default 15 minutes). PollInterval is how often
// the adapter status is read (default 10 seconds).
ActivationTimeout time.Duration
PollInterval time.Duration
// LeaseTTL and Heartbeat protect the operation across processes
// (defaults 2 minutes and 30 seconds).
LeaseTTL time.Duration
Heartbeat time.Duration
// HealthLinks are absolute URLs that must answer 2xx after activation.
HealthLinks []string
// Evidence providers, supplied by the host (nil ones record
// "unavailable" and fail the phase that needs them).
Compatibility func(ctx context.Context, target string) (Report, error)
Backup func(ctx context.Context) (Report, error)
Health func(ctx context.Context) (Report, error)
Link func(ctx context.Context, url string) error
// Schema lists the applied migrations ("module/name"); the difference
// before and after activation decides whether the prior artifact is
// still compatible with the current data.
Schema func(ctx context.Context) ([]string, error)
}
Config wires the lifecycle to the host. Only RunningVersion and Source are required for requests; a nil Adapter means manual activation.
type CoolifyAdapter ¶
type CoolifyAdapter struct {
// contains filtered or unexported fields
}
CoolifyAdapter requests a deployment of one operator-configured resource and reads the deployment status. The endpoint, token and resource come from server configuration; request parameters cannot change them.
func NewCoolifyAdapter ¶
func NewCoolifyAdapter(rawURL, token, resource string, client *http.Client) (*CoolifyAdapter, error)
NewCoolifyAdapter validates the operator configuration (HTTPS origin only, no credentials, query or path) and returns the adapter.
func (*CoolifyAdapter) Activate ¶
Activate pins the resource's image tag to the artifact (Docker image resources only), then posts the deploy request and requires the provider to echo the configured resource with a deployment id.
func (*CoolifyAdapter) CanActivate ¶
func (c *CoolifyAdapter) CanActivate(ctx context.Context, a Artifact) error
CanActivate reports whether the resource is a Docker image application, the only Coolify build pack whose target can be pinned to an artifact. Resources that build from a repository deploy whatever the branch holds, so they are refused rather than reported as a version-directed update.
func (*CoolifyAdapter) Name ¶
func (c *CoolifyAdapter) Name() string
func (*CoolifyAdapter) Status ¶
func (c *CoolifyAdapter) Status(ctx context.Context, ref string) (Activation, error)
Status reads GET /api/v1/deployments/{id}. Coolify statuses map to: finished → active; failed/cancelled → failed; queued → queued; in_progress → running; others → unknown.
type ErrConflict ¶
ErrConflict reports an operation or lease that blocks a new request.
func (*ErrConflict) Error ¶
func (e *ErrConflict) Error() string
type ErrLeaseHeld ¶
ErrLeaseHeld reports that another operation owns the data directory.
func (*ErrLeaseHeld) Error ¶
func (e *ErrLeaseHeld) Error() string
type Holder ¶
type Holder struct {
Holder, Owner, OperationID, ExpiresAt string
}
Holder describes the current lease.
type Lease ¶
type Lease struct {
// contains filtered or unexported fields
}
Stored expiries have second precision and are inclusive: a lease is live through its expiry second.
Lease is the durable ownership record. A process-local mutex or a time cooldown cannot protect a restarted process; the row with its owner token and expiry can. Acquire takes a free or expired lease, or one already held by the same owner; Renew extends it only for its owner.
type ManualAdapter ¶
type ManualAdapter struct{}
ManualAdapter is the fallback when no provider is configured: an operator replaces the binary or image themselves. Activate records the request; Status stays queued until the serving identity changes (which Reconcile observes) or the activation timeout expires.
func (ManualAdapter) Name ¶
func (ManualAdapter) Name() string
func (ManualAdapter) Status ¶
func (ManualAdapter) Status(context.Context, string) (Activation, error)
type Operation ¶
type Operation struct {
ID string `json:"id"`
Status string `json:"status" enum:"pending,running,succeeded,failed,interrupted,rolled_back,rollback_failed"`
CurrentPhase string `json:"current_phase"`
RequestedVersion string `json:"requested_version" doc:"Target asked for"`
RunningVersion string `json:"running_version" doc:"Serving identity when the operation was requested"`
ActivatedVersion *string `json:"activated_version" doc:"Identity confirmed by the serving process after activation; null until then"`
AcceptedVersion *string `json:"accepted_version" doc:"Target durably accepted after every required check passed; null otherwise"`
ExpectedDigest string `json:"expected_digest"`
Artifact *Artifact `json:"artifact" doc:"Verified release identity; null before verification"`
Previous *Artifact `json:"previous" doc:"Prior artifact eligible for runtime rollback on current data; null when unknown"`
BackupStamp string `json:"backup_stamp"`
DeploymentRef string `json:"deployment_ref" doc:"Provider reference; accepted is not active"`
RollbackRef string `json:"rollback_ref"`
MigrationState string `` /* 135-byte string literal not displayed */
Source string `json:"source"`
Adapter string `json:"adapter"`
RequestedBy string `json:"requested_by"`
Error string `json:"error"`
Remediation string `json:"remediation"`
Retryable bool `json:"retryable"`
HeartbeatAt *string `json:"heartbeat_at"`
CreatedAt string `json:"created_at"`
StartedAt *string `json:"started_at"`
FinishedAt *string `json:"finished_at"`
UpdatedAt string `json:"updated_at"`
Phases []Phase `json:"phases"`
}
Operation is a persisted update with its phases.
type Phase ¶
type Phase struct {
Name string `json:"name"`
Status string `json:"status" enum:"pending,running,passed,failed,skipped,interrupted"`
StartedAt *string `json:"started_at"`
FinishedAt *string `json:"finished_at"`
DurationMS *int64 `json:"duration_ms" doc:"Null until the phase finished"`
Progress *float64 `json:"progress" doc:"0..1 when measurable, else null"`
Attempts int `json:"attempts"`
Result json.RawMessage `json:"result" doc:"Phase evidence; never credentials or hosts"`
Error string `json:"error"`
Remediation string `json:"remediation"`
Retryable bool `json:"retryable"`
}
Phase is one reported lifecycle stage.
type ReleaseIndex ¶
type ReleaseIndex struct {
// contains filtered or unexported fields
}
ReleaseIndex reads an operator-trusted HTTPS JSON document:
{"latest":"0.15.0",
"releases":[{"version":"0.15.0","artifacts":[{"id":"ghcr.io/zadstack/gorun:0.15.0","digest":"sha256:…"}]}]}
Artifact selects which artifact of a release to use (exact id, or a substring when the release lists several); a release with one artifact needs no selector.
func NewLoopbackReleaseIndex ¶
func NewLoopbackReleaseIndex(rawURL, token, artifact string, client *http.Client) (*ReleaseIndex, error)
NewLoopbackReleaseIndex is NewReleaseIndex for a plain-HTTP index served on the local host (a local supervisor); any other scheme/host is refused.
func NewReleaseIndex ¶
func NewReleaseIndex(rawURL, token, artifact string, client *http.Client) (*ReleaseIndex, error)
NewReleaseIndex validates the HTTPS URL (no credentials, no fragment).
func (*ReleaseIndex) Name ¶
func (r *ReleaseIndex) Name() string
type Request ¶
type Request struct {
Version string `json:"version" doc:"Release version to activate, e.g. 0.15.0"`
ExpectedDigest string `json:"expected_digest,omitempty" doc:"Optional sha256:… the verified artifact must match"`
RequestedBy string `json:"-"`
}
Request asks for an update to Version.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service runs operations.
func (*Service) AdapterName ¶
AdapterName returns the configured deployment adapter name.
func (*Service) Configured ¶
Configured reports whether a trusted release source exists.
func (*Service) Reconcile ¶
Reconcile repairs operations left pending or running by a stopped process. atBoot says every previous runner is dead (one process per data directory); otherwise only operations whose heartbeat is older than the lease TTL are touched. An activation whose expected identity now serves passes on that evidence and the lifecycle continues in this process; an activation still inside its timeout is resumed; anything else is marked interrupted, never left looking successful or in progress.
func (*Service) Request ¶
Request validates, records and queues an operation, returning at once. A pending or running operation, a held lease, or a prior deployment the provider still reports in progress is a conflict.
func (*Service) RunningVersion ¶
RunningVersion is the serving identity.
func (*Service) SourceName ¶
SourceName and AdapterName describe the configured integrations.