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

fleet

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package fleet is the operator's write domain over the registry and the renderer: editing a workload's labels, changing the enforcement mode of a set of workloads, previewing what a selector resolves to, and rendering a hypothetical policy set without persisting it. The command line and the operator surface call these functions and nothing else, which is what keeps the two transports from drifting (ADR-0007 as amended, ADR-0021).

A mode change is a fan-out mutation: it resolves its selector through the renderer's own scope match, records the intent as the set it resolved to, flips the desired mode of exactly that set, and renders, all in one transaction under the render lock (ADR-0018). What it returns is an acknowledgment of the recorded intent, never a job: convergence is observed on each workload's applied version against its latest (ADR-0007 as amended). A dry run and a preview are pure reads of persisted state: no lock is held across them and nothing is written.

A directed reconnect asks the replica holding one workload's stream to send it a Reconnect directive, over the notification bridge renders announce on (ADR-0018 as amended). It writes nothing: there is no presence table, so nothing can say for certain that a stream exists, and the directive is a request whose outcome is observed as the workload's last snapshot instant advancing.

Retirement is one way (ADR-0023): in one transaction under the render lock it stamps the workload's row, closes an open agent silence as a stored gap, renders without the workload, and directs its live stream ended. Afterwards no selector resolves to the workload and every write naming it is refused with the instant.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrMatchCountMismatch is returned when a mode change's expected
	// match count is not what the selector resolved to; the error
	// carrying it is a *MatchCountError with both numbers.
	ErrMatchCountMismatch = errors.New("fleet: the selector did not resolve to the expected number of workloads")
	// ErrBadRequest is the kind of every admission failure of a request
	// here; the error carrying it is a *policy.Findings with the paths.
	ErrBadRequest = errors.New("fleet: the request is not admissible")
)

Errors a caller branches on.

View Source
var (
	ErrModeChangeSelection = errors.New("fleet: exactly one of selector or workload_ids must be given")
	ErrModeChangeMode      = errors.New("fleet: target_mode must be visibility, simulation, or enforced")
	ErrModeChangeExpected  = errors.New("fleet: expected_match_count is required and must not be negative")
	ErrUnknownWorkload     = errors.New("fleet: workload is not registered")
)

Errors of a mode change request's shape.

View Source
var ErrAgentOffline = errors.New("fleet: the workload's agent is offline; nothing was sent")

ErrAgentOffline is returned when a directed reconnect is refused because the workload's agent is offline as the read model judges it; the error carrying it is an *AgentOfflineError with the last-seen instant.

View Source
var ErrDuplicateLabelKey = policy.ErrDuplicateLabelKey

ErrDuplicateLabelKey is a label key given twice in one set.

Functions

func ValidateLabels

func ValidateLabels(labels []registry.Label) error

ValidateLabels admits a label set: every key and value in the label grammar, no key twice (ADR-0022).

Types

type AgentOfflineError

type AgentOfflineError struct {
	LastSeenAt *time.Time
}

AgentOfflineError is a refused directed reconnect with the instant the agent was last heard from, nil when it never was.

func (*AgentOfflineError) Error

func (e *AgentOfflineError) Error() string

func (*AgentOfflineError) Unwrap

func (e *AgentOfflineError) Unwrap() error

type Directives

type Directives interface {
	// DirectReconnect asks for a Reconnect directive on the stream of id.
	// It returns once the request is published; whether any replica
	// holds the stream is not known to it.
	DirectReconnect(ctx context.Context, id identity.WorkloadID) error
}

Directives sends a directive toward the stream of one workload. The store implements it over the notification channel render announcements ride, keyed by workload, and the replica holding that workload's stream sends the directive (ADR-0018 as amended).

type DryRunRequest

type DryRunRequest struct {
	Rulesets     []policy.Ruleset
	StateVersion string
}

DryRunRequest is a hypothetical policy set: the complete set of rulesets as they would be authored (services and address groups are the persisted ones, referenced by id), and the state version the author read before editing, if any.

type DryRunResult

type DryRunResult struct {
	StateVersion string
	Stale        bool
	Workloads    []compiler.WorkloadDiff
}

DryRunResult is what rendering the hypothetical set would change, against the state version it was computed on. Stale reports whether the request named a different version, so the author knows the state moved under them; the diff is still against what is persisted now.

type MatchCountError

type MatchCountError struct {
	Expected int
	Matched  int
}

MatchCountError is a refused mode change: the operator expected one number of workloads and the control plane resolved another. Both are reported so the operator can see what moved.

func (*MatchCountError) Error

func (e *MatchCountError) Error() string

func (*MatchCountError) Unwrap

func (e *MatchCountError) Unwrap() error

type ModeChangeRecord

type ModeChangeRecord struct {
	ID                 uuid.UUID
	CreatedAt          time.Time
	TargetMode         innerwallv1.EnforcementMode
	Selector           policy.Selector
	ExpectedMatchCount int
	Workloads          []ModeChangeWorkload
	DesiredUpdated     int
}

ModeChangeRecord is the recorded intent: the request as submitted and the set it resolved to at the instant it was applied.

type ModeChangeRequest

type ModeChangeRequest struct {
	Selector           policy.Selector
	WorkloadIDs        []identity.WorkloadID
	TargetMode         innerwallv1.EnforcementMode
	ExpectedMatchCount int
}

ModeChangeRequest is what the operator submits: exactly one of a selector or a list of workload ids, the mode to reach, and the number of workloads the operator expects the selection to be.

type ModeChangeResult

type ModeChangeResult struct {
	ID             uuid.UUID
	Matched        int
	DesiredUpdated int
	Rendered       *compiler.Report
}

ModeChangeResult acknowledges a recorded mode change. Matched is the size of the resolved set; DesiredUpdated is how many of them were not already in the target mode. Rendered is the render's report: which workloads' versions advanced. None of it says anything about convergence.

type ModeChangeTx

type ModeChangeTx interface {
	compiler.Tx
	// RecordModeChange persists the intent and the set it resolved to.
	RecordModeChange(ctx context.Context, rec *ModeChangeRecord) error
	// SetWorkloadModes sets the desired mode of exactly the given
	// workloads and reports how many were not already in it.
	SetWorkloadModes(ctx context.Context, ids []identity.WorkloadID, mode innerwallv1.EnforcementMode) (int, error)
}

ModeChangeTx is the store's side of one mode change: the render transaction, plus the recording of the intent and the flip of the resolved set by explicit id.

type ModeChangeWorkload

type ModeChangeWorkload struct {
	ID           identity.WorkloadID
	PreviousMode innerwallv1.EnforcementMode
}

ModeChangeWorkload is one workload of a recorded set with the mode it held before the change.

type Preview

type Preview struct {
	Matched []WorkloadRef
}

Preview is what a selector resolves to now.

type ReconnectRequest

type ReconnectRequest struct {
	LastSnapshotSentAt *time.Time
}

ReconnectRequest acknowledges a directed reconnect. LastSnapshotSentAt is the workload's snapshot instant as it stood when the directive was fired, nil when none has been recorded: the caller observes the outcome as a later read showing it advanced.

type RetireSubject added in v0.5.0

type RetireSubject struct {
	RetiredAt        *time.Time
	LastFlowReportAt *time.Time
	Process          *registry.AgentProcess
}

RetireSubject is what a retirement reads from the workload's row, under its lock: whether and when it was already retired, and what the silence derivation needs, the last flow report's receipt and the agent process the latest heartbeat proved running (each nil when none is recorded).

type RetireTx added in v0.5.0

type RetireTx interface {
	compiler.Tx
	// LockWorkload locks the workload's row for the rest of the
	// transaction and reads it, or returns registry.ErrWorkloadUnknown.
	// Flow ingest's receipt and label writes take the same lock.
	LockWorkload(ctx context.Context, id identity.WorkloadID) (*RetireSubject, error)
	// StampRetired records the retirement instant.
	StampRetired(ctx context.Context, id identity.WorkloadID, at time.Time) error
	// WriteGaps stores derived gaps through the flow store, inside this
	// transaction.
	WriteGaps(ctx context.Context, id identity.WorkloadID, gaps []flowstore.Gap) error
	// DirectEnd asks every replica, once the transaction commits, to end
	// the workload's live stream if it holds it. Delivery is best-effort
	// by design, as for every directive (ADR-0018 as amended).
	DirectEnd(ctx context.Context, id identity.WorkloadID) error
}

RetireTx is the store's side of one retirement: the render transaction, plus the locked row, the stamp, the closed silence, and the directive that ends the workload's live stream, all committed together.

type Retirement added in v0.5.0

type Retirement struct {
	ID        identity.WorkloadID
	RetiredAt time.Time
	Rendered  *compiler.Report
	Closed    *flowstore.Gap
}

Retirement acknowledges one retirement: the instant, the render it triggered, and the silence it closed as a stored gap, nil when none was open.

type Service

type Service struct {
	Store  Store
	Engine *compiler.Engine
	// Reads is the read model a directed reconnect judges the workload
	// by, and Directives is where it sends one; RequestReconnect needs
	// both, nothing else here uses them.
	Reads      *readmodel.Reader
	Directives Directives
	// Streams ends the live streams this process holds when a workload
	// is retired; nil in a process that holds none.
	Streams Streams
	// FlowWindow is the flow window agents are handed, which judges the
	// silence a retirement closes (ADR-0019 as amended); with none, no
	// silence is closed.
	FlowWindow time.Duration
	Log        *slog.Logger
	// Now is the clock; time.Now if nil.
	Now func() time.Time
}

Service is the fleet write domain.

func (*Service) AuthoringState

func (s *Service) AuthoringState(ctx context.Context) ([]policy.Ruleset, string, error)

AuthoringState is every ruleset and the version of the state they were read in, from one consistent snapshot: the pair an editor authors against. Read separately, a write landing between the two would pair rulesets from before it with a version from after, and a dry run of what the editor read would not be reported stale.

func (*Service) ChangeMode

func (s *Service) ChangeMode(ctx context.Context, req ModeChangeRequest, hasExpected bool) (*ModeChangeResult, error)

ChangeMode applies a mode change: in one transaction under the render lock it resolves the selection against the registry as it stands, refuses when the resolved count is not the expected one, records the intent with the resolved set, flips the desired mode of exactly that set by id, and renders so that every affected workload's version advances and is announced on commit. A selector resolves to active workloads only; an explicit id naming a retired workload is refused with a *registry.RetiredError, since its mode is frozen (ADR-0023). A label change committed by another process meanwhile cannot move the set: the flip names the ids the resolution produced, never the selector again.

func (*Service) DryRun

func (s *Service) DryRun(ctx context.Context, req DryRunRequest) (*DryRunResult, error)

DryRun renders the hypothetical set against persisted state and diffs it against the persisted rendered policies, then discards everything. The hypothetical rulesets are admitted exactly as a write would admit them, so a dry run that passes here is one the write path would accept.

func (*Service) PreviewSelector

func (s *Service) PreviewSelector(ctx context.Context, sel policy.Selector) (*Preview, error)

PreviewSelector resolves a selector against the registry through the renderer's scope match, exactly as a mode change resolves it: retired workloads match no selector.

func (*Service) RequestReconnect

func (s *Service) RequestReconnect(ctx context.Context, id identity.WorkloadID) (*ReconnectRequest, error)

RequestReconnect directs the agent of one workload to reconnect, so that its stream restarts from a fresh snapshot. It refuses an unknown workload with registry.ErrWorkloadUnknown, a retired one with a *registry.RetiredError (its identity is refused at every door, so there is nothing to reconnect; ADR-0023), and refuses with an *AgentOfflineError when the read model's sync state says the agent is offline. That judgment is the one the fleet screens show, recorded when a stream closes; it approximates whether a stream exists and is not a presence check, which nothing can make (ADR-0018 as amended). Otherwise it fires the directive over the notification bridge and returns without waiting: delivery is best-effort by design, and whether it worked is read later as the snapshot instant advancing.

func (*Service) Retire added in v0.5.0

func (s *Service) Retire(ctx context.Context, id identity.WorkloadID) (*Retirement, error)

Retire retires a workload (ADR-0023). It is one way. In one transaction under the render lock it locks the workload's row, refuses an unknown workload (registry.ErrWorkloadUnknown) and one already retired (a *registry.RetiredError naming the instant), stamps the retirement, closes an open agent silence as a stored gap by ADR-0019's derivation at the retirement instant, renders so that the workload's addresses leave every peer set and every workload whose output changed advances, and publishes the directive that ends its live stream on whichever replica holds it. Once committed, a stream this process holds is ended directly. The row stays, so history keeps naming the identity. The caller is the principal its context carries; one without is refused.

func (*Service) SetLabels

func (s *Service) SetLabels(ctx context.Context, id identity.WorkloadID, labels []registry.Label, expect string) error

SetLabels replaces a workload's labels and renders, since a label change moves the workload in and out of selectors (ADR-0018). expect is the version of the labels the caller read; empty writes unconditionally. A retired workload's labels are frozen, and the write is refused with a *registry.RetiredError (ADR-0023).

func (*Service) StateVersion

func (s *Service) StateVersion(ctx context.Context) (string, error)

StateVersion is the version of the state a render reads now, for a caller that will author against it.

type Store

type Store interface {
	// ListWorkloads returns every workload with its labels.
	ListWorkloads(ctx context.Context) ([]registry.Workload, error)
	// ReplaceWorkloadLabels replaces a workload's labels when they still
	// hold the version the caller read (registry.LabelsVersion), refusing
	// with a *registry.LabelsVersionError otherwise; an empty expect
	// replaces unconditionally. It returns registry.ErrWorkloadUnknown
	// for an unregistered id.
	ReplaceWorkloadLabels(ctx context.Context, id identity.WorkloadID, labels []registry.Label, expect string) error
	// ModeChangeTx runs fn in one transaction holding the render lock and
	// commits when fn returns nil.
	ModeChangeTx(ctx context.Context, fn func(ctx context.Context, tx ModeChangeTx) error) error
	// LoadRenderState reads the render inputs and every persisted policy
	// as one consistent snapshot, without a lock and without writing.
	LoadRenderState(ctx context.Context) (*compiler.Inputs, map[identity.WorkloadID]*innerwallv1.WorkloadPolicy, error)
	// RetireTx runs fn in one transaction holding the render lock and
	// commits when fn returns nil.
	RetireTx(ctx context.Context, fn func(ctx context.Context, tx RetireTx) error) error
}

Store is the persistence the fleet domain needs, implemented with hand-written SQL in internal/store (ADR-0006).

type Streams added in v0.5.0

type Streams interface {
	// EndRetired ends the live stream of a retired workload, if this
	// process holds it, with the refusal every door gives a retired
	// identity, and reports whether it held one.
	EndRetired(id identity.WorkloadID) bool
}

Streams ends the live sync streams this process holds. The gateway implements it; a process that holds no streams, such as the command line, has none, and the directive alone reaches the replica that does.

type WorkloadRef

type WorkloadRef struct {
	ID       identity.WorkloadID
	Hostname string
	Labels   map[string]string
}

WorkloadRef names a workload a selector resolved to.

Directories

Path Synopsis
Package fleettest holds an in-memory double of the stores the authoring and fleet domains write through (policy.Store, fleet.Store, and the compiler's render transaction), for tests of those domains and of the transports above them.
Package fleettest holds an in-memory double of the stores the authoring and fleet domains write through (policy.Store, fleet.Store, and the compiler's render transaction), for tests of those domains and of the transports above them.

Jump to

Keyboard shortcuts

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