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

updater

package
v0.2.49 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: GPL-3.0 Imports: 22 Imported by: 0

Documentation

Overview

Package updater implements Ether's signed self-update feed.

A release publishes one JSON manifest per channel ("stable" / "beta"). The manifest lists per-platform artifacts (a bare ether binary and/or a platform installer) with SHA-256 checksums, and carries an ed25519 signature — embedded in the "signature" field, or detached in a sibling "<manifest>.sig" file. The embedded signature covers the canonical JSON encoding of the manifest with the signature field emptied; a detached signature covers the exact published manifest bytes. The release public key is compiled in (PublicKeyB64, set via -ldflags); cmd/ether-release-sign generates keys and signs manifests in CI.

Nothing unsigned or mis-checksummed is ever applied: Check verifies the signature before comparing versions, and Download verifies size and SHA-256 before Apply touches the installed binary.

Index

Constants

View Source
const (
	StateIdle          = "idle"
	StateDownloading   = "downloading"
	StateStaged        = "staged"
	StateWaitingWindow = "waiting-for-window"
	StateWaitingIdle   = "waiting-for-idle"
	StateApplying      = "applying"
	StateRolledBack    = "rolled-back"
	// Clustered nodes only (see Coordinator).
	StateWaitingTurn = "waiting-for-turn" // another node holds the update lease
	StateDraining    = "draining"         // lease held; waiting for this node's sessions to end
)

Manager states reported by Status.

View Source
const (
	CapabilitySelfApply  = "self-apply"
	CapabilityNotifyOnly = "notify-only"
	CapabilityDisabled   = "disabled"
)

Capabilities reported by Status.

View Source
const (
	DefaultPollInterval    = 24 * time.Hour
	DefaultFirstCheckDelay = 5 * time.Minute
	DefaultRetryInterval   = time.Hour
	DefaultQuietPeriod     = 10 * time.Minute
	DefaultHealthPeriod    = 10 * time.Minute
	DefaultTick            = 30 * time.Second
	DefaultWindowStart     = "03:00"
	DefaultWindowEnd       = "06:00"
	// DefaultMaxDrain bounds a clustered node's drain before an install.
	DefaultMaxDrain = 30 * time.Minute
)

Manager defaults.

View Source
const (
	ChannelStable = "stable"
	ChannelBeta   = "beta"
)

Channels.

View Source
const (
	KindBinary    = "binary"    // bare ether executable; atomic self-replace
	KindInstaller = "installer" // .dmg/.pkg/.exe/.msi/.deb/.rpm; downloaded + launched
	KindArchive   = "archive"   // .tar.gz/.zip bundle; downloaded, not applied automatically
)

Artifact kinds.

View Source
const DefaultFeedBase = "https://github.com/braedonsaunders/ether/releases/download/channels"

DefaultFeedBase is where release manifests live when config has no override: <base>/<channel>.json (+ optional <channel>.json.sig). The release workflow keeps every channel's current manifest on the rolling "channels" release: GitHub's releases/latest never points at a beta, so a beta manifest cannot be found there.

View Source
const (

	// DefaultMaxStartAttempts is how many starts of a freshly installed
	// version may happen without it confirming health before the previous
	// binary is restored.
	DefaultMaxStartAttempts = 3
)

Marker files inside the updates directory (<dataDir>/updates).

View Source
const ManifestLifetime = 30 * 24 * time.Hour

ManifestLifetime bounds replay to 30 days. The publisher refreshes the signature weekly without changing the release, artifacts, or publication date.

Variables

View Source
var (
	// ErrUpdatesDisabled: this build cannot verify updates (no release key)
	// or the feed settings are unusable.
	ErrUpdatesDisabled = errors.New("updater: updates are disabled in this build")
	// ErrNotSelfApplicable: the install is notify-only (package manager,
	// container, macOS app bundle, read-only install dir, ...).
	ErrNotSelfApplicable = errors.New("updater: this installation cannot update itself")
	// ErrClusterRolledBack is returned by Coordinator.AcquireUpdate for a
	// release that rolled back on another node of the cluster.
	ErrClusterRolledBack = errors.New("updater: this release was rolled back on another cluster node")
)
View Source
var (
	ErrBadSignature   = errors.New("updater: manifest signature verification failed")
	ErrNoPublicKey    = errors.New("updater: no release public key configured")
	ErrNoArtifact     = errors.New("updater: no artifact for this platform")
	ErrChecksum       = errors.New("updater: artifact checksum mismatch")
	ErrTooOldToUpdate = errors.New("updater: installed version is too old to update directly; reinstall")
	ErrStaleManifest  = errors.New("updater: update feed is expired or lacks signed freshness; check the server clock and contact the feed publisher")
)
View Source
var (
	Version = "dev"
	Commit  = ""
	Date    = ""

	// PublicKeyB64 is the base64 (std encoding) ed25519 public key that
	// release manifests are signed with. Injected at release build time;
	// empty in dev builds, in which case Check requires an explicit key.
	PublicKeyB64 = ""
)

Build metadata, set by goreleaser via -ldflags -X. Version is a semver string ("1.2.3", optionally "v1.2.3-beta.1"); "dev" in development builds.

Functions

func ApplyBinary

func ApplyBinary(newPath, target string) error

ApplyBinary atomically replaces target with the verified new binary at newPath, keeping the previous binary at "<target>.old".

Unix: the new file is staged next to the target and rename(2)d over it — running processes keep their inode, so this is safe mid-run. Windows cannot overwrite a running executable, so the current binary is first renamed aside to .old (allowed while running), then the new one moved in; on failure the .old is moved back.

func CleanupOld

func CleanupOld(target string) error

CleanupOld removes a stale "<target>.old" left by a previous update. The server calls this at startup once the new binary is confirmed running (on Windows the .old of a *running* binary can only be deleted by the next process, which is exactly this).

func CompareVersions

func CompareVersions(a, b string) (int, error)

CompareVersions returns -1, 0 or 1 (a <, ==, > b) per semver precedence: a release outranks its prereleases; numeric prerelease identifiers compare numerically and rank below alphanumeric ones.

func DefaultPublicKey

func DefaultPublicKey() (ed25519.PublicKey, error)

DefaultPublicKey returns the compiled-in release public key, if any.

func DirWritable

func DirWritable(dir string) bool

DirWritable reports whether this process can create files in dir.

func EncodeKey

func EncodeKey(k []byte) string

EncodeKey renders a key as base64 for storage/-ldflags.

func FailedPath

func FailedPath(dir string) string

func GenerateKeyPair

func GenerateKeyPair() (pub ed25519.PublicKey, priv ed25519.PrivateKey, err error)

GenerateKeyPair creates a fresh ed25519 release-signing key pair.

func InContainer

func InContainer() bool

InContainer reports whether we are running inside a container image, which must be updated by pulling a new image, never in place.

func IsDev

func IsDev() bool

IsDev reports whether this is an unversioned development build.

func Newer

func Newer(candidate, current string) bool

Newer reports whether candidate is strictly newer than current. An unparseable/dev current version makes every valid candidate newer.

func PendingPath

func PendingPath(dir string) string

PendingPath / FailedPath locate the marker files inside dir.

func PrivateKeyFromB64

func PrivateKeyFromB64(s string) (ed25519.PrivateKey, error)

PrivateKeyFromB64 parses a base64 private key (64-byte ed25519 form, or a 32-byte seed which is expanded).

func PublicKeyFromB64

func PublicKeyFromB64(s string) (ed25519.PublicKey, error)

PublicKeyFromB64 parses a base64 public key (as baked in via -ldflags).

func Rollback

func Rollback(target string) error

Rollback restores "<target>.old" over target. On Windows the caller must not be running from target (the service should be stopped first).

func SelfApplyCapability

func SelfApplyCapability(env Environment) (bool, string)

SelfApplyCapability decides whether this install may replace its own binary. Anything owned by something else — a package manager, a container image, a signed macOS app bundle — is notify-only.

func ShouldAutoApply

func ShouldAutoApply(upd config.Updates) bool

ShouldAutoApply interprets the owner's update policy: auto-apply only when config says so; the tray/server otherwise prompts.

func SignDetached

func SignDetached(priv ed25519.PrivateKey, manifestBytes []byte) (string, error)

SignDetached returns a base64 signature over the exact manifest file bytes, for publishing next to (not inside) the manifest.

func VerifyDetached

func VerifyDetached(pub ed25519.PublicKey, manifestBytes []byte, sigB64 string) error

VerifyDetached checks a detached base64 (or raw) signature over the exact bytes the manifest was served as.

func VerifyFile

func VerifyFile(path string, art *Artifact) error

VerifyFile checks an already-downloaded file against an artifact.

Types

type ApplyOptions

type ApplyOptions struct {
	// Target is the installed binary replaced for KindBinary updates.
	// Empty: os.Executable().
	Target string
	// DownloadDir for the artifact. Empty: temp dir.
	DownloadDir string
	// LaunchInstaller starts a downloaded installer/.dmg. Nil: the
	// platform default (exec the installer / `open` on macOS).
	LaunchInstaller func(ctx context.Context, path string) error
	Progress        ProgressFunc
}

ApplyOptions tunes Apply.

type Artifact

type Artifact struct {
	OS       string `json:"os"`   // runtime.GOOS
	Arch     string `json:"arch"` // runtime.GOARCH, or "universal" (macOS)
	Kind     string `json:"kind"` // KindBinary | KindInstaller | KindArchive
	URL      string `json:"url"`
	SHA256   string `json:"sha256"` // hex, lowercase
	Size     int64  `json:"size"`
	Filename string `json:"filename,omitempty"`
}

Artifact is one downloadable file for one platform.

type AvailableUpdate

type AvailableUpdate struct {
	Version     string     `json:"version"`
	Kind        string     `json:"kind"`
	NotesURL    string     `json:"notesUrl,omitempty"`
	Notes       string     `json:"notes,omitempty"`
	PublishedAt *time.Time `json:"publishedAt,omitempty"`
	Staged      bool       `json:"staged"`
}

AvailableUpdate describes the newest release on the feed.

type Checker

type Checker interface {
	Check(ctx context.Context) (*Update, error)
	Download(ctx context.Context, upd *Update, dir string, progress ProgressFunc) (string, error)
}

Checker is the slice of *Client the Manager needs.

type Client

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

Client checks, downloads and applies updates from the signed feed.

func NewClient

func NewClient(opts Options) (*Client, error)

NewClient builds a Client. It fails fast when no usable public key is configured: an updater that cannot verify must never update.

func NewClientFromConfig

func NewClientFromConfig(upd config.Updates, logger *slog.Logger) (*Client, error)

NewClientFromConfig maps the owner's Updates settings onto a Client.

func (*Client) Apply

func (c *Client) Apply(ctx context.Context, upd *Update, opts ApplyOptions) (string, error)

Apply downloads (and verifies) the update, then:

  • KindBinary: atomically replaces the target binary, keeping "<target>.old" for rollback. The caller must restart the process.
  • KindInstaller: launches the installer and returns; the installer owns stopping/restarting the service.
  • KindArchive: returns the downloaded path without applying.

It returns the artifact file path.

func (*Client) Check

func (c *Client) Check(ctx context.Context) (*Update, error)

Check fetches and verifies the channel manifest. It returns (nil, nil) when the feed's version is not newer than the running one.

func (*Client) CurrentVersion

func (c *Client) CurrentVersion() string

CurrentVersion is the version Check compares against.

func (*Client) Download

func (c *Client) Download(ctx context.Context, upd *Update, dir string, progress ProgressFunc) (string, error)

Download fetches the update's artifact into dir (empty: tempDir()), verifying size and SHA-256. The returned file is complete and verified; partial files are removed on error. Downloads are capped at max(Size, Size*1.1) when Size is known, else 2 GiB.

func (*Client) DownloadArtifact

func (c *Client) DownloadArtifact(ctx context.Context, art *Artifact, dir string, progress ProgressFunc) (string, error)

DownloadArtifact is Download for a bare Artifact.

func (*Client) ManifestURL

func (c *Client) ManifestURL() string

ManifestURL resolves the feed URL for the configured channel:

  • a URL containing "{channel}" has it substituted;
  • a URL ending in ".json" is used as-is;
  • anything else is a base: "<base>/<channel>.json".

func (*Client) Poll

func (c *Client) Poll(ctx context.Context, interval, firstDelay time.Duration, onUpdate func(context.Context, *Update))

Poll periodically checks the feed until ctx ends, invoking onUpdate for each newly-seen update version. Check errors are logged and retried on the next tick; the first check runs immediately unless firstDelay > 0.

type ClusterUpdateStatus

type ClusterUpdateStatus struct {
	// LeaseHeld is true while this node holds the cluster update lease
	// for an install.
	LeaseHeld bool `json:"leaseHeld"`
	// LeaseHolder is the node last seen holding the lease (this node's
	// name when LeaseHeld).
	LeaseHolder     string     `json:"leaseHolder,omitempty"`
	Draining        bool       `json:"draining"`
	DrainStarted    *time.Time `json:"drainStarted,omitempty"`
	MaxDrainSeconds int        `json:"maxDrainSeconds"`
}

ClusterUpdateStatus is a clustered node's part in a rolling update.

type Coordinator

type Coordinator interface {
	// AcquireUpdate takes, or renews, the update lease for version. ok is
	// false while another node (holder) holds it; err wraps
	// ErrClusterRolledBack for a release that rolled back elsewhere.
	AcquireUpdate(ctx context.Context, version string) (ok bool, holder string, err error)
	// ReleaseUpdate gives the lease back if this node holds it.
	ReleaseUpdate(ctx context.Context) error
	// ReportFailed records that version rolled back on this node.
	ReportFailed(ctx context.Context, version string) error
	// SetDraining takes this node out of (true) or back into (false) the
	// rotation for new sessions.
	SetDraining(draining bool)
}

Coordinator serialises self-updates across a cluster (rolling updates). With a Coordinator the manager installs a release only while holding the cluster-wide update lease, so nodes restart one at a time; before installing it drains this node (no new sessions, out of the published connections) and waits for this node's own sessions to end, up to Updates.MaxDrain. The lease stays held through the restart: the new process renews it while confirming its health and releases it once confirmed. A process that finds no update to confirm releases a lease this node still holds; one that rolled back reports the failed release so the other nodes skip it.

type Environment

type Environment struct {
	GOOS        string
	Target      string // running executable
	Dev         bool   // IsDev()
	InContainer bool
	// Supervised: a service manager restarts the process after it exits.
	Supervised bool
	// DirWritable probes the executable's directory (default: DirWritable).
	DirWritable func(dir string) bool
}

Environment describes the install for SelfApplyCapability.

type FailedUpdate

type FailedUpdate struct {
	Version string    `json:"version"`
	From    string    `json:"from"`
	At      time.Time `json:"at"`
	Reason  string    `json:"reason"`
	// ReportPending is set by a rollback and cleared once a clustered node
	// has told the other nodes to skip Version. A successful rollback
	// restarts straight into the restored binary, so it is that process
	// (with no update to confirm) that reports.
	ReportPending bool `json:"reportPending,omitempty"`
}

FailedUpdate records a version that was rolled back; the manager skips exactly this version until a newer one is published.

type Manager

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

Manager polls the signed feed and, when allowed, downloads a release early and installs it at a safe moment: inside the maintenance window (unless the owner pressed "Update now") and after the server has been idle for the quiet period (unless forced).

func NewManager

func NewManager(opts ManagerOptions) *Manager

NewManager builds a Manager; it does not start polling (see Run).

func (*Manager) Enabled

func (m *Manager) Enabled() bool

Enabled reports whether update checks run.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context)

Run drives the manager until ctx ends or a restart has been requested.

func (*Manager) Status

func (m *Manager) Status() ManagerStatus

Status snapshots the manager.

func (*Manager) UpdateNow

func (m *Manager) UpdateNow(force bool) error

UpdateNow asks for the available update (checking the feed first if none is known) to be installed as soon as the server is idle, ignoring the maintenance window. force also skips the idle wait.

type ManagerOptions

type ManagerOptions struct {
	// NewClient builds the feed client for the current settings; it is
	// rebuilt when channel/feed change. Nil or failing at construction:
	// update checks are disabled (startup confirmation still runs).
	NewClient func(config.Updates) (Checker, error)
	// Config returns the live Updates settings.
	Config func() config.Updates
	// CurrentVersion is the running version (default: Version).
	CurrentVersion string
	// Dir is <dataDir>/updates: downloads and marker files.
	Dir string
	// Target is the running server executable that a binary update
	// replaces (default: os.Executable()).
	Target string
	// SelfApply reports whether this install may replace its own binary;
	// NotifyReason explains why not. See SelfApplyCapability.
	SelfApply    bool
	NotifyReason string
	// ActiveSessions returns how many playback / transcode / live TV
	// sessions are in progress. Nil: always idle.
	ActiveSessions func() int
	// RequestRestart asks the app to shut down gracefully and exit so the
	// service manager starts the new binary. Called at most once.
	RequestRestart func(reason string)
	// Boot is the result of ConfirmStartup for this process.
	Boot StartupResult
	// Coordinator, when set (a clustered node that can self-apply),
	// coordinates installs with the other nodes; nil keeps the standalone
	// behaviour (install after the server has been idle for QuietPeriod).
	Coordinator Coordinator
	// NodeName labels this node in status (the lease holder is compared
	// with it); optional.
	NodeName string
	// OnAvailable, when set, hears each release version a check finds,
	// without the manager's lock held. Once it returns nil for a version it
	// is not called for that version again in this process; an error makes
	// the next check that finds the version call it again.
	OnAvailable func(version string) error

	Now             func() time.Time
	Location        *time.Location // maintenance window zone (default time.Local)
	PollInterval    time.Duration
	FirstCheckDelay time.Duration
	RetryInterval   time.Duration
	QuietPeriod     time.Duration
	HealthPeriod    time.Duration
	Tick            time.Duration
	// ApplyBinary / CleanupOld default to the package functions.
	ApplyBinary func(newPath, target string) error
	CleanupOld  func(target string) error
	Logger      *slog.Logger
}

ManagerOptions configures a Manager. Only Dir and Config are required.

type ManagerStatus

type ManagerStatus struct {
	Enabled            bool             `json:"enabled"`
	DisabledReason     string           `json:"disabledReason,omitempty"`
	Current            string           `json:"current"`
	Available          *AvailableUpdate `json:"available,omitempty"`
	Capability         string           `json:"capability"`
	CapabilityReason   string           `json:"capabilityReason,omitempty"`
	State              string           `json:"state"`
	AutoUpdate         bool             `json:"autoUpdate"`
	Requested          bool             `json:"requested"`
	Force              bool             `json:"force"`
	LastCheck          *time.Time       `json:"lastCheck,omitempty"`
	NextCheck          *time.Time       `json:"nextCheck,omitempty"`
	LastError          string           `json:"lastError,omitempty"`
	Window             Window           `json:"window"`
	ActiveSessions     int              `json:"activeSessions"`
	IdleSince          *time.Time       `json:"idleSince,omitempty"`
	QuietPeriodSeconds int              `json:"quietPeriodSeconds"`
	Confirming         bool             `json:"confirming"`
	FailedVersion      string           `json:"failedVersion,omitempty"`
	FailedReason       string           `json:"failedReason,omitempty"`
	Note               string           `json:"note,omitempty"`
	// Cluster is the rolling-update state of a clustered node.
	Cluster *ClusterUpdateStatus `json:"cluster,omitempty"`
}

ManagerStatus is the admin-facing snapshot.

type Manifest

type Manifest struct {
	Version     string    `json:"version"`
	Channel     string    `json:"channel"`
	PublishedAt time.Time `json:"publishedAt,omitempty"`
	// IssuedAt and ExpiresAt bound replay of a signed feed. Pointers preserve
	// the canonical encoding of legacy manifests during signature checks.
	IssuedAt  *time.Time `json:"issuedAt,omitempty"`
	ExpiresAt *time.Time `json:"expiresAt,omitempty"`
	NotesURL  string     `json:"notesUrl,omitempty"`
	Notes     string     `json:"notes,omitempty"`
	// MinVersion, when set, is the oldest installed version that may
	// update directly to this release (older installs must reinstall).
	MinVersion string     `json:"minVersion,omitempty"`
	Artifacts  []Artifact `json:"artifacts"`
	// Signature is base64 ed25519 over SigningBytes(). Empty when the
	// signature is detached (served from "<manifest URL>.sig").
	Signature string `json:"signature,omitempty"`
}

Manifest is the signed release document for one channel.

func (*Manifest) ArtifactFor

func (m *Manifest) ArtifactFor(goos, goarch string, prefKinds ...string) (*Artifact, error)

ArtifactFor picks the artifact for goos/goarch, preferring a bare binary, then an installer, then an archive. "universal" arch matches any arch on the same OS (macOS universal binaries). prefKinds overrides the default preference order when non-empty.

func (*Manifest) RenewFreshness added in v0.2.4

func (m *Manifest) RenewFreshness(now time.Time)

RenewFreshness updates the timestamps before signing. Never call this on unverified downloaded content: renewal is a release-publisher operation.

func (*Manifest) Sign

func (m *Manifest) Sign(priv ed25519.PrivateKey) error

Sign sets m.Signature using priv.

func (*Manifest) SigningBytes

func (m *Manifest) SigningBytes() ([]byte, error)

SigningBytes is the canonical payload an embedded signature covers: the manifest's JSON encoding (struct field order, no indentation) with the Signature field emptied.

func (*Manifest) Validate

func (m *Manifest) Validate() error

Validate checks the manifest is well-formed (not authentic — see Verify).

func (*Manifest) ValidateFreshness added in v0.2.4

func (m *Manifest) ValidateFreshness(now time.Time) error

ValidateFreshness must run only after authenticating the manifest.

func (*Manifest) Verify

func (m *Manifest) Verify(pub ed25519.PublicKey) error

Verify checks the embedded signature against pub.

type Options

type Options struct {
	// FeedURL is the manifest URL, feed base, or a template containing
	// "{channel}". See ManifestURL for resolution. Empty: DefaultFeedBase.
	FeedURL string
	// Channel: "stable" (default) or "beta".
	Channel string
	// CurrentVersion is the running version. Empty: the build Version.
	CurrentVersion string
	// PublicKey verifies manifests. Nil: the compiled-in key (required).
	PublicKey ed25519.PublicKey
	// HTTPClient for feed + artifact fetches. Nil: a 30s-timeout client
	// for manifests; downloads set their own per-request deadline via ctx.
	HTTPClient *http.Client
	// GOOS/GOARCH override artifact selection (tests, cross-tooling).
	GOOS, GOARCH string
	// PreferKinds overrides artifact kind preference (e.g. installer-only
	// on macOS where the .dmg is the product).
	PreferKinds []string
	Logger      *slog.Logger
}

Options configures a Client. Zero values fall back to build defaults.

type Pending

type Pending struct {
	From      string    `json:"from"`
	To        string    `json:"to"`
	Target    string    `json:"target"`
	AppliedAt time.Time `json:"appliedAt"`
	Attempts  int       `json:"attempts"`
}

Pending is written immediately before a binary swap and removed once the new version has served healthily for the health period. Attempts counts starts of the new version; a crash loop pushes it past the limit and triggers a rollback.

type ProgressFunc

type ProgressFunc func(downloaded, total int64)

ProgressFunc receives cumulative downloaded bytes (total may be 0 when the manifest omits Size).

type Semver

type Semver struct {
	Major, Minor, Patch uint64
	Pre                 []string // prerelease identifiers, empty for a release
	// contains filtered or unexported fields
}

Semver is a parsed semantic version.

func ParseVersion

func ParseVersion(s string) (Semver, error)

ParseVersion parses "v1.2.3-beta.1+build". Missing minor/patch default to 0 ("1.2" == 1.2.0). Build metadata is accepted and ignored.

func (Semver) Compare

func (v Semver) Compare(o Semver) int

Compare is CompareVersions on parsed values.

func (Semver) String

func (v Semver) String() string

type StartupOptions

type StartupOptions struct {
	// Dir is the updates directory holding pending.json / failed.json.
	Dir string
	// Target is the running executable; used when the marker has none and
	// for leftover cleanup. Empty: os.Executable().
	Target string
	// CurrentVersion is the running build's version.
	CurrentVersion string
	// MaxAttempts before rollback. <= 0: DefaultMaxStartAttempts.
	MaxAttempts int
	Now         func() time.Time
	// Rollback / CleanupOld default to the package functions.
	Rollback   func(target string) error
	CleanupOld func(target string) error
	Logger     *slog.Logger
}

StartupOptions configures ConfirmStartup.

type StartupResult

type StartupResult struct {
	// Confirming is the marker of a just-installed update that this
	// process (the new version) must confirm by running healthily.
	Confirming *Pending
	// RolledBack is set when the previous binary was restored. When
	// RestartRequired is also set the process must exit so the service
	// manager starts the restored binary.
	RolledBack      *FailedUpdate
	RestartRequired bool
	// Note is a human-readable summary for logs and the admin status.
	Note string
}

StartupResult is what ConfirmStartup found and did.

func ConfirmStartup

func ConfirmStartup(opts StartupOptions) (StartupResult, error)

ConfirmStartup runs once at process start, before anything that a bad release could break (config parse, database migrations), and implements confirm-or-rollback for a self-applied update:

  • no marker: remove leftover "<target>.old"/".bad" from a confirmed update;
  • marker and Version == To: count this start; past MaxAttempts the update is considered crash-looping, the previous binary is restored, the version is recorded as failed and a restart is requested;
  • marker and Version == From: the swap never took effect; clear it.

type Update

type Update struct {
	Manifest *Manifest
	Artifact *Artifact
}

Update is a verified, newer release with the artifact for this platform.

func (*Update) Version

func (u *Update) Version() string

Version is the update's version.

type Window

type Window struct {
	Start string `json:"start"`
	End   string `json:"end"`
	Open  bool   `json:"open"`
}

Window is the maintenance window as reported by Status.

Jump to

Keyboard shortcuts

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