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 ¶
- Variables
- func ValidateLabels(labels []registry.Label) error
- type AgentOfflineError
- type Directives
- type DryRunRequest
- type DryRunResult
- type MatchCountError
- type ModeChangeRecord
- type ModeChangeRequest
- type ModeChangeResult
- type ModeChangeTx
- type ModeChangeWorkload
- type Preview
- type ReconnectRequest
- type RetireSubject
- type RetireTx
- type Retirement
- type Service
- func (s *Service) AuthoringState(ctx context.Context) ([]policy.Ruleset, string, error)
- func (s *Service) ChangeMode(ctx context.Context, req ModeChangeRequest, hasExpected bool) (*ModeChangeResult, error)
- func (s *Service) DryRun(ctx context.Context, req DryRunRequest) (*DryRunResult, error)
- func (s *Service) PreviewSelector(ctx context.Context, sel policy.Selector) (*Preview, error)
- func (s *Service) RequestReconnect(ctx context.Context, id identity.WorkloadID) (*ReconnectRequest, error)
- func (s *Service) Retire(ctx context.Context, id identity.WorkloadID) (*Retirement, error)
- func (s *Service) SetLabels(ctx context.Context, id identity.WorkloadID, labels []registry.Label, ...) error
- func (s *Service) StateVersion(ctx context.Context) (string, error)
- type Store
- type Streams
- type WorkloadRef
Constants ¶
This section is empty.
Variables ¶
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.
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.
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.
var ErrDuplicateLabelKey = policy.ErrDuplicateLabelKey
ErrDuplicateLabelKey is a label key given twice in one set.
Functions ¶
func ValidateLabels ¶
ValidateLabels admits a label set: every key and value in the label grammar, no key twice (ADR-0022).
Types ¶
type AgentOfflineError ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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).
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. |