Documentation
¶
Overview ¶
Package selfupdate implements in-process binary self-update for the runner.
Index ¶
- func BinaryVersion(ctx context.Context, path string) string
- func NewerVersion(candidate, own string) bool
- func RestartInto(path string) error
- func SeedCanonical(exe, canonical string) error
- func ShouldSkipTarget(exePath, target string) bool
- type Marker
- type Outcome
- type ProbationMgr
- type StatusFunc
- type Target
- type Updater
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BinaryVersion ¶ added in v0.0.19
BinaryVersion runs `<path> version` and parses the first line, which every released runner prints as "flashduty-runner <version>". Returns "" (not an error) when the file doesn't exist, so callers can treat absent-canonical and unreadable-version identically: don't redirect.
func NewerVersion ¶ added in v0.0.19
NewerVersion reports whether candidate is a parseable version strictly newer than own. Either side unparseable → false: a dev build never trampolines, and a canonical binary whose version can't be read is never exec'd into.
func RestartInto ¶ added in v0.0.19
RestartInto hands the process over to the binary at path (exec on unix, spawn-and-exit on windows). Exposed for the boot trampoline in cmd.
func SeedCanonical ¶ added in v0.0.19
SeedCanonical copies the running (old) binary to the canonical path if nothing is there yet. Run before the first relocated Apply so the swap's .bak snapshot — and therefore probation rollback — has a real predecessor: rolling back from a bad new version lands on this copy of the version that was running, not on a missing file. Copies via a temp file + rename so a crash mid-copy can never leave a truncated binary at the canonical path.
func ShouldSkipTarget ¶
ShouldSkipTarget reports whether an advertised target should be ignored because this host already rolled back from exactly that version.
Types ¶
type Marker ¶
type Marker struct {
TargetVersion string `json:"target_version"`
Attempts int `json:"attempts"`
RolledBack bool `json:"rolled_back"`
}
Marker persists across re-exec/restart to drive the probation/rollback state machine. A live marker means "an upgrade to TargetVersion is on probation"; it is deleted on commit (handshake) and carries RolledBack=true across the rollback re-exec so the restored old binary can report the outcome once.
type Outcome ¶
type Outcome struct {
InProbation bool // main should Commit() after first successful handshake
}
Outcome tells main what to do after CheckOnBoot.
type ProbationMgr ¶
type ProbationMgr struct {
ExePath string
CurrentVersion string
MaxAttempts int
// contains filtered or unexported fields
}
ProbationMgr runs the boot-time probation/rollback state machine driven by the persisted upgrade marker. reExec is an injectable seam so tests never actually restart; nil means the real platform restartSelf.
func NewProbationMgr ¶
func NewProbationMgr(exePath, currentVersion string, maxAttempts int) *ProbationMgr
NewProbationMgr returns a ProbationMgr for the given canonical executable.
func (*ProbationMgr) CheckOnBoot ¶
func (p *ProbationMgr) CheckOnBoot() (Outcome, error)
CheckOnBoot inspects the marker at startup and advances the probation state machine: no marker -> normal; committed/stale -> cleanup; rolled_back -> report; matching target not yet committed -> probation (attempts++ then rollback past MaxAttempts).
func (*ProbationMgr) Commit ¶
func (p *ProbationMgr) Commit()
Commit is called by main after the first successful WS handshake during probation. It clears the marker, the .bak rollback copy, and any prior failed-version memory (we've successfully moved to a new version, so older failures are moot).
func (*ProbationMgr) RollbackNow ¶
func (p *ProbationMgr) RollbackNow()
RollbackNow restores the previous binary and re-execs into it. main calls this when the probation deadline elapses without a successful handshake (failure mode ②: the new binary boots but cannot complete the WS handshake — auth/url/ protocol regression). It is independent of the boot-attempt counter, which can never fire under the unlimited-reconnect (maxAttempts=0) config because the process never exits.
type StatusFunc ¶
type StatusFunc func(phase, errMsg string)
StatusFunc reports an upgrade phase (and optional error message) to the caller.
type Target ¶ added in v0.0.19
type Target struct {
Exe string // running executable, symlinks resolved
Path string // upgrade target; "" if no writable location exists at all
Relocated bool // Path is the canonical state-home fallback, not Exe
}
Target is where self-update operates for this process.
Path is the binary path upgrades download to, swap at, and re-exec from — and therefore also where the probation marker, .bak rollback copy, and failed-version memory live (layoutFor anchors on it). When the running executable's own directory is writable, Path == Exe and upgrades swap in place (systemd state-dir installs, root installs, dev builds). When it is not — a root-owned /usr/local/bin binary run as a regular user — Path falls back to the canonical runner-owned location <state home>/bin, which the boot trampoline in cmd/main.go redirects into on every later start.
func ResolveTarget ¶ added in v0.0.19
ResolveTarget decides where self-update should operate, given the running executable path and the runner state home (environment.RunnerStateHome()).
exePath is symlink-resolved first: on Linux os.Executable() already returns the /proc/self/exe target, but on darwin it returns the invocation path, so an installer layout that symlinks /usr/local/bin -> <state dir>/bin would otherwise be misjudged by the writability probe.
The canonical fallback directory is created on demand; this is the same <state home>/bin that holds the bundled fduty CLI, guaranteed writable by the runner's own user. Note it deliberately ignores FLASHDUTY_RUNNER_BIN_DIR: that override exists to point bash's PATH at an image-baked fduty (e.g. /usr/local/bin on cloud images) and may well be read-only — the runner binary's home must stay in the user-writable state tree.
type Updater ¶
type Updater struct {
ExePath string
// contains filtered or unexported fields
}
Updater performs an in-process self-update: download, verify, extract, atomic swap, marker write, and restart. reExec is an injectable seam so tests never actually restart; nil means the real platform restartSelf (exec on unix, spawn-and-exit on windows).
func NewUpdater ¶
NewUpdater returns an Updater bound to the binary path upgrades operate on — Target.Path from ResolveTarget: the running executable for in-place updates, or the canonical state-home location for relocated ones.
func (*Updater) Apply ¶
func (u *Updater) Apply(ctx context.Context, p protocol.UpgradePayload, status StatusFunc) error
Apply downloads the target artifact, verifies it, atomically swaps it into place, writes the probation marker, and re-execs. On any failure before the swap it leaves the running binary untouched; on a post-swap re-exec failure it rolls back inline.