instance

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package instance defines the Instance resource kind: a record of one running flynn process. Promoting a process to a stored resource makes it listable and describable through the same read model as every other kind, and gives placement and remote control a concrete handle to address. An Instance carries declarative spec (its host, version, and the capabilities it offers) and reconciled status (its current run-state and the runs it is driving). The status is written by the live process, never by a user, and the resource's own last-write time on the envelope is the heartbeat, so a stale record is a missed heartbeat.

Index

Constants

View Source
const (
	// GroupVersion is the Instance kind's API group and version.
	GroupVersion = "instance.ionagent.io/v1alpha1"
	// Kind is the resource kind name.
	Kind = "Instance"
)
View Source
const DefaultHeartbeatInterval = 30 * time.Second

DefaultHeartbeatInterval is how often a live process refreshes its Instance record. It is well under DefaultStaleAfter so a single missed beat never marks a healthy process stale: the staleness window spans several intervals, and only a process that has stopped writing entirely crosses it.

View Source
const DefaultStaleAfter = 90 * time.Second

DefaultStaleAfter is how long an instance's heartbeat (its last envelope write) may age before its live run-state can no longer be trusted and is reported as Unknown. A healthy process refreshes its record well within this window; exceeding it means the process most likely stopped without recording a terminal state, so reporting its last live state as current would be a lie. The value is several heartbeat intervals, so a single missed refresh does not flap the state.

Variables

This section is empty.

Functions

func IsStale

func IsStale(r resource.Resource, now time.Time, staleAfter time.Duration) bool

IsStale reports whether the instance's heartbeat is older than staleAfter as of now. The heartbeat is the resource's last envelope write time (UpdatedAt): a live process refreshes it on every Register and SetStatus, so a record that has not moved is a process that has stopped writing. A non-positive staleAfter disables the check (nothing is ever stale), and a record that was never written (zero UpdatedAt) is treated as stale, because there is no heartbeat to trust. The comparison is strict, so a heartbeat exactly staleAfter old is not yet stale.

func Register

func Register(ctx context.Context, store resource.Store, scope resource.Scope, id string, spec Spec) (resource.Resource, error)

Register upserts this process's Instance resource by id, recording its declared spec and refreshing its heartbeat (the envelope write time). An existing record's status is preserved, so re-registering on startup never clears a live run-state; a brand new instance starts Idle. It returns the stored resource.

func RegisterKind

func RegisterKind(reg *resource.Registry) error

RegisterKind registers the Instance kind so instances can be stored and admitted like any other resource.

func SetStatus

func SetStatus(ctx context.Context, store resource.Store, scope resource.Scope, id string, state State, runs []string) (resource.Resource, error)

SetStatus writes the instance's reconciled status (its run-state and active runs) without changing its spec, refreshing the heartbeat. It preserves the stored spec by reading the current record first, so status and spec are written by their respective owners and never clobber each other.

Types

type Heartbeat

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

Heartbeat keeps a live process's Instance record current. It registers the process on start, writes its observed state once immediately, then on each interval re-observes and rewrites the record. Every write refreshes the heartbeat (the envelope's write time), so a record that stops moving is a process that has stopped, which the effective-state rule reports as Unknown rather than leaving frozen at its last live state. On a clean stop the process records a terminal Done, so a deliberate shutdown is distinguished from a crash.

Heartbeat is the write half of the read surface: flynn ps/status, the dashboard, and the remote API all read the records it maintains. Without it those views show a process as perpetually Idle with a heartbeat that only advances when observed, which is exactly backwards.

func NewHeartbeat

func NewHeartbeat(store resource.Store, scope resource.Scope, id string, spec Spec, report Reporter, clk clock.Timing, opts ...Option) *Heartbeat

NewHeartbeat builds a Heartbeat for this process's Instance record. id is the stable instance id (the store's InstanceID), spec its declared shape, report the observer of its current work, and clk the time source (System in production, a Manual clock under test so the loop is deterministic and never sleeps).

func (*Heartbeat) Run

func (h *Heartbeat) Run(ctx context.Context) error

Run registers the instance, writes its state once so the record is correct from the start (never lingering on a stale state preserved from a previous process life), then beats on the interval until ctx is cancelled. On cancellation it records a terminal Done with a fresh context, so a cleanly stopped process reads Done rather than decaying to Unknown. Run returns the registration error if the initial Register fails (a process that cannot announce itself should surface it); per-beat errors go to the error handler and never stop the loop.

type Option

type Option func(*Heartbeat)

Option configures a Heartbeat.

func WithErrorHandler

func WithErrorHandler(fn func(error)) Option

WithErrorHandler installs a callback for the transient store errors a beat may hit. A heartbeat must outlive a single failed write, so by default an error is swallowed and the next beat retries; a caller that wants to surface or count them supplies a handler. A nil handler restores the default (ignore).

func WithInterval

func WithInterval(d time.Duration) Option

WithInterval sets the beat interval. A non-positive interval is ignored, leaving the default, so a misconfiguration never produces a busy loop.

type Reporter

type Reporter func(ctx context.Context) (State, []string)

Reporter observes what this process is doing right now and returns the run-state to record together with the ids of the runs driving it. It is injected so the instance package stays decoupled from the goal and run domains: the caller, which knows how to read its own work, supplies the derivation. A Reporter must be pure of side effects and quick; it is called on every beat. Returning StateUnknown is the honest answer when the caller cannot determine its own state.

type Spec

type Spec struct {
	Host         string   `json:"host,omitempty"`
	Version      string   `json:"version,omitempty"`
	Capabilities []string `json:"capabilities,omitempty"`
}

Spec is an instance's declared shape: where it runs, what version it is, and the capabilities it can offer work. Every field is optional, so a minimal Instance is just its name (the instance id).

func DecodeSpec

func DecodeSpec(r resource.Resource) (Spec, error)

DecodeSpec reads the typed spec from a resource.

type State

type State string

State is the coarse run-state of an instance: the vocabulary the read surface reports for what a process is doing right now.

const (
	// StateIdle is a registered instance with no active run.
	StateIdle State = "Idle"
	// StateWorking is an instance driving one or more runs.
	StateWorking State = "Working"
	// StateBlocked is an instance whose runs are all waiting (on approval or input).
	StateBlocked State = "Blocked"
	// StateDone is an instance that has finished its work and is shutting down.
	StateDone State = "Done"
	// StateUnknown is an instance whose state cannot be determined (for example a
	// heartbeat too old to trust).
	StateUnknown State = "Unknown"
)

func EffectiveState

func EffectiveState(r resource.Resource, now time.Time, staleAfter time.Duration) State

EffectiveState is the run-state to report for an instance right now, derived from its recorded status and the age of its heartbeat. It is the single rule the read surface (flynn ps/status, the dashboard, the remote API) shares, so a crashed process is never reported as alive: a live recorded state (Idle, Working, Blocked) becomes Unknown once the heartbeat goes stale. A cleanly finished instance stays Done regardless of heartbeat age, because it recorded its own terminal state before shutting down and is expected to stop refreshing. A record whose status is missing or unreadable reports Unknown rather than guessing. The function is pure (time enters only through now), so it is deterministic under replay and the same result on every machine.

type Status

type Status struct {
	State              State    `json:"state,omitempty"`
	Runs               []string `json:"runs,omitempty"`
	ObservedGeneration int64    `json:"observedGeneration,omitempty"`
}

Status is an instance's observed state, written by the live process. Runs lists the run ids the instance is currently driving; the heartbeat is the resource's envelope write time, not a field here.

func DecodeStatus

func DecodeStatus(r resource.Resource) (Status, error)

DecodeStatus reads the typed status from a resource.

Jump to

Keyboard shortcuts

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