executorstore

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package executorstore backs the remote-executor enrollment flow with SQLite.

It is a type-converting adapter and nothing else: pkg/statedb owns the rows and the SQL, pkg/executor/remote owns the protocol and the crypto, and this package joins them. Keeping it separate keeps two dependencies apart that neither side wants:

  • pkg/executor/remote must not depend on SQLite. The agent binary running on an edge device imports it, and linking a database engine into a process whose whole job is to fork workloads is dead weight on exactly the hardware that can least afford it.
  • pkg/statedb must not depend on pkg/executor/remote, which pulls in a WebSocket stack a storage layer has no business knowing about.

The duplication of record shapes across the boundary is the price of that separation, and it is a small and stable price: these structs change only when the enrollment schema does.

Index

Constants

View Source
const (
	SourceAgentCredential = "agent credential"
	SourceConfiguration   = "configuration"
)

Record sources, for the messages that explain a match or a take-back.

View Source
const (
	AuditEntityExecutor   = "executor"
	AuditEventStateChange = auditaction.ActionExecutorStateChange
	AuditEventFailover    = auditaction.ActionExecutorFailover
	AuditEventExhausted   = auditaction.ActionExecutorFailoverExhausted
	AuditActorSupervisor  = "supervisor"
)

Audit constants for fleet events. EntityType is "executor" so an operator can filter the audit log down to infrastructure changes with one predicate.

Variables

This section is empty.

Functions

func FailoverProjectPath added in v0.0.5

func FailoverProjectPath(sess executor.Session) string

FailoverProjectPath is the hub's own path to the project a session belongs to. A session dispatched to an executor that does not share the hub's filesystem records the executor's work directory as its ProjectPath — a path relative to the device's work root, meaningless on the hub — while the project label every hub dispatch carries names the hub's path (uiSpec). The label wins; the recorded path is the fallback for sessions dispatched without one.

func NewClaimToken

func NewClaimToken() (string, error)

NewClaimToken mints a session claim token.

The token arbitrates a race; it is not a secret, and anything that can write to the database can rewrite the row directly. crypto/rand is used anyway because it costs nothing here and removes the need to reason about whether a predictable token could ever let a stale supervisor guess its way back into ownership of a session it no longer holds.

func NewSessionID

func NewSessionID() (string, error)

NewSessionID mints a session identifier. It is random rather than sequential so session IDs carry no information about fleet size or dispatch rate.

Types

type FleetMember added in v0.0.7

type FleetMember struct {
	ID   string
	Name string
	Kind string
	// Device is set for a virtual executor: the device whose labels it
	// carries.
	Device string
	Record secretbroker.RecordedExecutor
}

FleetMember is one executor the hub knows, with its record, for asking which executors a label selector reaches (Task 20432).

func Fleet added in v0.0.7

func Fleet(db *statedb.DB, records secretbroker.ExecutorRecords, extra []FleetMember) ([]FleetMember, error)

Fleet lists every executor with a registry row — the devices, their virtual executors, the configured drivers — and every one in extra that has none, each with records' record of it, sorted by ID.

func Reaches added in v0.0.7

func Reaches(members []FleetMember, sub secretbroker.Subject) []FleetMember

Reaches returns the members a grant to sub would be delivered to now: for a label selector, those whose recorded labels carry every pair it names. A project subject's reach is not a set of executors, nor "any"'s; both give nil.

type HandleLoss added in v0.0.5

type HandleLoss struct {
	// SessionID is the session the workload ran as, "" when none is recorded
	// (session tracking is best-effort: a dispatch whose row could not be
	// written runs untracked, and is never failed over).
	SessionID string
	// State is the session's recorded state.
	State string
	// Attempt is the session's dispatch count, 1 for a first dispatch.
	Attempt int
	// ClaimedAt is when the claim took the session, for a claimed one: the
	// time its state last changed.
	ClaimedAt time.Time
	// Successor is the session a failover opened to replace this one, and
	// SuccessorExecutor and SuccessorHandle where it runs; all "" when none.
	Successor         string
	SuccessorExecutor string
	SuccessorHandle   string
}

HandleLoss is what the session store says about one dispatched workload, looked up by its executor and handle (Task 20396): whether the failover claim took it off its executor, and what replaced it.

It exists because the hub follows a run by its executor and handle, while the supervisor that decides the run's fate claims it by session — and is often in another process. The store is where the two meet.

func (HandleLoss) Claimed added in v0.0.5

func (l HandleLoss) Claimed() bool

Claimed reports whether the failover claim took the session off its executor: requeued (re-dispatched, or with nowhere to go) or failover_exhausted. A claimed session's workload is no longer the run of its project, whatever its driver still says about it.

func (HandleLoss) Exhausted added in v0.0.5

func (l HandleLoss) Exhausted() bool

Exhausted reports whether the claim closed it past the failover cap.

func (HandleLoss) Replaced added in v0.0.5

func (l HandleLoss) Replaced() bool

Replaced reports whether a failover started a replacement for it.

type Handles

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

Handles implements executor.HandleStore over a *statedb.DB.

It is a separate type from Scheduler rather than more methods on it because the two have different owners: a Scheduler is the control plane's view and is constructed by the supervisor, while a Handles is handed to a *driver*, which is the one thing in this system that may also run outside the hub. Keeping them apart means a driver holds a store that can do exactly three things and cannot, say, close a session it does not own.

func NewHandles

func NewHandles(db *statedb.DB) (*Handles, error)

NewHandles wraps a database handle.

func (*Handles) ClaimHandleMeta added in v0.0.5

func (h *Handles) ClaimHandleMeta(handleID, key, expect, value string) (bool, error)

ClaimHandleMeta sets one meta key of a handle's row from expect to value, conditionally (Task 20419). See statedb.ClaimExecutorHandleMeta.

func (*Handles) DeleteHandle

func (h *Handles) DeleteHandle(handleID string) error

DeleteHandle implements executor.HandleStore.

func (*Handles) HandleMeta added in v0.0.5

func (h *Handles) HandleMeta(handleID, key string) (string, bool, error)

HandleMeta returns one meta key of a handle's row, and whether the row exists.

func (*Handles) ListHandles

func (h *Handles) ListHandles(executorID string) ([]executor.HandleRecord, error)

ListHandles implements executor.HandleStore.

func (*Handles) PutHandle

func (h *Handles) PutHandle(rec executor.HandleRecord) error

PutHandle implements executor.HandleStore.

type Records added in v0.0.7

type Records struct {
	// DB holds the agent credentials and the virtual executors.
	DB *statedb.DB
	// Configured returns the labels the hub's configuration gives each
	// configured executor it enables, by ID (config.ExecutorsConfig.
	// ConfiguredExecutorLabels). Asked on every lookup, so an edit to the
	// configuration applies to the next lease. Nil for a process that
	// configures no executor.
	Configured func() (map[string]map[string]string, error)
}

Records implements secretbroker.ExecutorRecords over the control plane's database and the hub's configuration.

func (Records) RecordedExecutor added in v0.0.7

func (r Records) RecordedExecutor(executorID string) (secretbroker.RecordedExecutor, error)

RecordedExecutor implements secretbroker.ExecutorRecords.

A table or a configuration that cannot be read is an error. Answering "no labels" instead would dispatch the run without the label grants its executor holds — the silent non-delivery Task 20432 exists to end — and say nothing about why.

type Scheduler

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

Scheduler implements executor.HealthStore, executor.SessionStore, and executor.EventSink over a *statedb.DB.

func NewScheduler

func NewScheduler(db *statedb.DB) (*Scheduler, error)

NewScheduler wraps a database handle.

func (*Scheduler) ClaimRequeue

func (s *Scheduler) ClaimRequeue(sessionID, claimToken string, maxAttempts int, at time.Time) (executor.Session, bool, error)

ClaimRequeue is the exactly-once failover latch.

It mints a fresh claim token and hands it to a single conditional UPDATE. The supervisor that wins gets the session back carrying the new token; every other caller — a concurrent supervisor, or this one after a restart replaying the same unreachable transition — presents a token that no longer matches and receives executor.ErrSessionClaimLost.

The same UPDATE applies the failover cap (Task 20391): a session already re-dispatched maxAttempts times is closed as failover_exhausted instead of requeued, and exhausted reports it. The winner learns the session's fate from the row the claim wrote, not from a check of its own, so it cannot disagree with what was recorded.

The sentinel is translated at this boundary for the same reason store.go translates the enrollment sentinels: the supervisor distinguishes "I lost the race, do nothing" from "the database is broken, complain", and collapsing both into an opaque storage error would turn a benign race into log noise that hides a real outage.

func (*Scheduler) CloseSession

func (s *Scheduler) CloseSession(sessionID, state string, at time.Time) error

CloseSession marks a session finished or failed.

func (*Scheduler) CountRunning

func (s *Scheduler) CountRunning(executorID string) (int, error)

CountRunning returns the number of in-flight sessions on an executor.

func (*Scheduler) ExecutorFailover

func (s *Scheduler) ExecutorFailover(ev executor.FailoverEvent)

ExecutorFailover records one session moved off a dead node, including the case where no replacement was found, and — as executor.failover_exhausted — the case where the cap allowed none (Task 20391).

func (*Scheduler) ExecutorTransition

func (s *Scheduler) ExecutorTransition(t executor.Transition)

ExecutorTransition records a state change in the audit log.

Failures are swallowed deliberately. The sink is called from probe goroutines, and an audit write that fails must not be able to stop the supervisor from *acting* on a node going unreachable — losing the log line is bad, losing the failover is worse.

func (*Scheduler) FailedOverProjects added in v0.0.5

func (s *Scheduler) FailedOverProjects() ([]string, error)

FailedOverProjects returns the hub path of every project a session was ever failed over or exhausted for (Task 20391), sorted: the projects that can hold a task quarantined as a suspected node killer.

func (*Scheduler) ForgetHealth

func (s *Scheduler) ForgetHealth(executorID string) error

ForgetHealth drops an executor's health record, for use when an executor is deleted. A stale record would otherwise resurrect as a phantom row in `cloop executor ls` naming a backend that no longer exists.

func (*Scheduler) ListHealth

func (s *Scheduler) ListHealth() ([]executor.Health, error)

ListHealth returns every persisted health record, ordered by executor ID.

func (*Scheduler) LoadHealth

func (s *Scheduler) LoadHealth(executorID string) (executor.Health, error)

LoadHealth returns the persisted health for an executor. An executor that has never been probed yields the zero Health and a nil error: "not yet observed" is a normal state for a backend registered from config a millisecond ago, not a lookup failure the supervisor should log.

func (*Scheduler) LossOfHandle added in v0.0.5

func (s *Scheduler) LossOfHandle(executorID, handleID string) (HandleLoss, error)

LossOfHandle reads what the store recorded about the workload handleID on executorID. A workload with no session yields the zero HandleLoss and a nil error: "never tracked" is a normal answer, not a failed lookup.

func (*Scheduler) LostNodes added in v0.0.5

func (s *Scheduler) LostNodes(sessionID string) ([]pm.NodeLoss, error)

LostNodes returns every node the run behind sessionID has been lost on so far, oldest first: one entry per session in its failover chain that a claim took off an unreachable executor — requeued or failover_exhausted — with when the claim found it gone (Task 20391).

func (*Scheduler) OpenRequeuedSession

func (s *Scheduler) OpenRequeuedSession(sess executor.Session, requeuedFrom string) error

OpenRequeuedSession records the replacement session created by a failover, linking it back to the session it replaces.

func (*Scheduler) OpenSession

func (s *Scheduler) OpenSession(sess executor.Session) error

OpenSession records a dispatched workload as in flight.

The caller supplies the session ID and claim token (see NewSessionID and NewClaimToken) because it needs the token in hand before the workload starts: a session that becomes visible to a supervisor before its owner knows the token is a session the owner can no longer defend against requeue.

func (*Scheduler) RunningSessions

func (s *Scheduler) RunningSessions(executorID string) ([]executor.Session, error)

RunningSessions returns the in-flight sessions on an executor. Passing "" returns every running session.

func (*Scheduler) RunningTasksOf added in v0.0.5

func (s *Scheduler) RunningTasksOf(sessionID string) ([]int, error)

RunningTasksOf returns the tasks a session last recorded as running.

func (*Scheduler) SaveHealth

func (s *Scheduler) SaveHealth(h executor.Health) error

SaveHealth persists a health record.

func (*Scheduler) SessionState added in v0.0.5

func (s *Scheduler) SessionState(sessionID string) (state string, replaced bool, err error)

SessionState returns a session's recorded state and whether a failover has already opened a replacement for it — the two facts a re-dispatch checks before starting anything (Task 20391).

func (*Scheduler) SetRunningTasks added in v0.0.5

func (s *Scheduler) SetRunningTasks(sessionID string, tasks []int) (bool, error)

SetRunningTasks records which tasks a running session's workload is working on (Task 20391). It reports whether the session was still running to record it on.

func (*Scheduler) WithActor

func (s *Scheduler) WithActor(actor string) *Scheduler

WithActor returns a copy that attributes audit entries to actor. Used by the CLI ("cli") and the Web UI (the authenticated subject) so a cordon is traceable to a person rather than to the supervisor that persisted it.

type Store

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

Store implements remote.Store over a *statedb.DB.

func New

func New(db *statedb.DB) (*Store, error)

New wraps a database handle.

func (*Store) GetAgent

func (s *Store) GetAgent(agentID string) (remote.AgentRecord, error)

GetAgent loads an agent by ID.

func (*Store) GetEnrollment

func (s *Store) GetEnrollment(id string) (remote.EnrollmentRecord, error)

GetEnrollment loads a token by ID.

func (*Store) ListAgents

func (s *Store) ListAgents() ([]remote.AgentRecord, error)

ListAgents returns every enrolled agent.

func (*Store) ListEnrollments

func (s *Store) ListEnrollments() ([]remote.EnrollmentRecord, error)

ListEnrollments returns every token, newest first.

func (*Store) PutAgent

func (s *Store) PutAgent(rec remote.AgentRecord) error

PutAgent persists an agent credential record.

func (*Store) PutEnrollment

func (s *Store) PutEnrollment(rec remote.EnrollmentRecord) error

PutEnrollment persists a minted token record.

func (*Store) RedeemEnrollment

func (s *Store) RedeemEnrollment(id, agentID string, at time.Time) error

RedeemEnrollment atomically claims a token.

func (*Store) RevokeAgent

func (s *Store) RevokeAgent(agentID string, at time.Time) error

RevokeAgent marks an agent's credential revoked.

func (*Store) RevokeEnrollment

func (s *Store) RevokeEnrollment(id string, at time.Time) error

RevokeEnrollment marks a token revoked.

func (*Store) TouchAgent

func (s *Store) TouchAgent(agentID string, at time.Time) error

TouchAgent records a successful authentication.

Jump to

Keyboard shortcuts

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