kernelforward

package
v4.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 42 Imported by: 0

Documentation

Overview

Package kernelforward is the kernel's forwarding state (docs/architecture/forward-sdk.md section 8, F3a): routes and their port and mark allocations, the node inventory with each Agent's reported capabilities, the planner run on every change (sdk/forward/planner), each node's desired state and generation, the nodes' latest reports and the traffic ledger. It serves the ForwardControl contract to official packages (kernel.forward.v1) and gives the Agent Control stream what it carries: the node's state for its anixops.nodeconfig/v2 configuration (NodeConfigMember) and the acceptance of its reports (RecordReport).

Every plan runs under one lock row (v4_kernel_forward_plan), so plans run one at a time across Control processes, and plans every route together: any violation refuses the whole plan and every node keeps its previous generation (forward-sdk.md section 5.1). A route write that the plan refuses is refused; a replan after an inventory change that is refused is recorded (PlanStatus) and logged.

Index

Constants

View Source
const (
	// DefaultDiagnoseTimeout is a diagnosis's budget when the request names
	// none; MaxDiagnoseTimeout caps the budget a request asks for, below
	// the package host's request timeout (30 s).
	DefaultDiagnoseTimeout = 15 * time.Second
	MaxDiagnoseTimeout     = 25 * time.Second

	// DiagnoseCacheTTL is how long a finished diagnosis is answered again
	// (cached) for the same route, which also rate-limits each route.
	DiagnoseCacheTTL = 10 * time.Second
	// MaxConcurrentDiagnoses bounds the routes diagnosed at once in one
	// Control process; one more is ErrDiagnoseBusy.
	MaxConcurrentDiagnoses = 4
)

Diagnosis bounds.

View Source
const (
	CodeRoutePaused            = "route_paused"
	CodeRouteEnforced          = "route_enforced"
	CodeNotPlanned             = "not_planned"
	CodeNeverReported          = "never_reported"
	CodeNotApplied             = "not_applied"
	CodeApplyFailed            = "apply_failed"
	CodeHopError               = "hop_error"
	CodeHealthy                = "healthy"
	CodeUnhealthy              = "unhealthy"
	CodeCircuitOpen            = "circuit_open"
	CodeNoHealth               = "no_health"
	CodeNodeVantageUnavailable = "node_vantage_unavailable"
	CodeNodeOffline            = "node_offline"
	CodeTargetNotAllowed       = "target_not_allowed"
	CodeNotPublic              = "not_public"
	CodeDeadline               = "deadline"
	CodeAgentTimeout           = "agent_timeout"
	CodeAgentError             = "agent_error"
	CodeReachable              = "reachable"
	CodeUnreachable            = "unreachable"
	CodeControlUDP             = "control_udp_not_probed"
)

Stable step codes (ProbeResult.code).

View Source
const (
	CheckOK           = "ok"
	CheckFailed       = "failed"
	CheckInconclusive = "inconclusive"
	CheckSkipped      = "skipped"
)

Forward check statuses, as the Agent answers them (CheckResult.Status, CheckItem.Status).

View Source
const (
	DNSDefaultTTL = 60
	DNSMaxTTL     = 86400
)

Binding defaults and bounds.

View Source
const (
	DNSModeDDNS  = "ddns"
	DNSModeCNAME = "cname"
)

Binding modes and record types as stored.

View Source
const (
	CodeSecretStoreUnavailable = "secret_store_unavailable"
	CodeProviderInUse          = "provider_in_use"
	CodeBindingExists          = "binding_exists"
	CodeNameTaken              = "name_taken"
	CodeUnknownRoute           = "unknown_route"
	CodeUnknownProvider        = "unknown_provider"
	CodeEntryHostnameRequired  = "entry_hostname_required"
	CodeHostnameMismatch       = "hostname_mismatch"
	CodeImmutable              = "immutable"
	CodeInvalidFormat          = "invalid_format"
	CodeRequired               = "required"
	CodeUnknownField           = "unknown_field"
)

Violation codes of the DNS writes.

View Source
const (
	DNSEvaluateInterval   = 10 * time.Second
	DNSRemoveAfter        = 3
	DNSAddAfter           = 3
	DNSReportStaleAfter   = 150 * time.Second
	DNSPresenceStaleAfter = 180 * time.Second
	DNSMinPublishInterval = 30 * time.Second
	DNSResyncInterval     = time.Hour
	DNSRetryBase          = 30 * time.Second
	DNSRetryMax           = 15 * time.Minute
	DNSProviderBurst      = 10
	DNSProviderRefill     = 6 * time.Second
)

The entry HA controller (forward-sdk.md section 7.4, L2). Every DNSEvaluateInterval the singleton worker evaluates each binding's entry nodes and keeps the binding's records on the addresses of the nodes in rotation:

  • A node is healthy when it is in the inventory with a public address of a published family, its latest forward report arrived within DNSReportStaleAfter, its Agent is online (a session in this process, or a stream seen within DNSPresenceStaleAfter by any process), the report has no hop error on the route's entry hop, and not every upstream of the entry hop is down. A node still converging (its report behind its desired generation, or not applied) is neither healthy nor unhealthy: its streaks do not move.
  • Hysteresis: a node leaves rotation after DNSRemoveAfter unhealthy evaluations in a row and joins after DNSAddAfter healthy ones; before the binding's first publication a healthy node joins at once.
  • Never empty: when no node is in rotation for a record type, the last published values are kept, the binding is degraded, a warning is logged and the change audited.
  • Provider calls are rate limited per provider (a token bucket of DNSProviderBurst calls refilled one per DNSProviderRefill), spaced at least DNSMinPublishInterval apart per binding, retried with an exponential back-off from DNSRetryBase to DNSRetryMax after a failure, and repeated every DNSResyncInterval to undo drift.
  • Every publication is audited (system/forward-dns) and counted.

The state lives in the binding and node tables, so a new lease holder carries on where the last one stopped.

View Source
const (
	RecoveryReport   = "report"
	RecoveryPlan     = "plan"
	RecoveryOperator = "operator"
)

Recovery reasons (anixops_forward_generation_recoveries_total).

View Source
const (
	// DefaultPortFirst and DefaultPortLast are the port range the planner
	// allocates from on a node without its own (decided by default with
	// F3a, as in the contract fixtures; the owner may revisit).
	DefaultPortFirst uint32 = 30000
	DefaultPortLast  uint32 = 39999
	// MaxNodeAddresses, MaxReservedPorts and MaxNodeLabels bound a node's
	// settings.
	MaxNodeAddresses = 16
	MaxReservedPorts = 1024
	MaxNodeLabels    = 32
)
View Source
const (
	// MaxTrafficWindow bounds GetTraffic's window.
	MaxTrafficWindow = 31 * 24 * time.Hour
	// MaxTrafficBuckets bounds one GetTraffic answer.
	MaxTrafficBuckets = 20000
)
View Source
const (
	// ReleaseGrace is how long a deleted route's ports and marks stay held
	// (forward-sdk.md section 5.2), so a late packet never reaches a new
	// route.
	ReleaseGrace = 10 * time.Minute
	// RequestRetention is how long a write's request id is remembered.
	RequestRetention = 7 * 24 * time.Hour
	// MaxRequestID bounds a request id.
	MaxRequestID = 128
)
View Source
const (
	EnforcedQuota   = "quota"
	EnforcedExpired = "expired"
)

Enforcement reasons (KernelForwardRoute.Enforced).

View Source
const MaintenanceInterval = time.Minute

MaintenanceInterval is how often the singleton worker maintains the forwarding state.

Variables

View Source
var (
	// ErrDNSProviderNotFound: no DNS provider with that id.
	ErrDNSProviderNotFound = errors.New("forward dns provider not found")
	// ErrDNSBindingNotFound: no DNS binding with that id.
	ErrDNSBindingNotFound = errors.New("forward dns binding not found")
	// ErrDNSPurge: a binding's published records could not be deleted.
	ErrDNSPurge = errors.New("forward dns: the provider did not delete the published records")
)
View Source
var (
	// ErrNotFound: no route with that id.
	ErrNotFound = errors.New("forward route not found")
	// ErrRevisionMismatch: UpdateRoute's expected_revision is not the
	// stored revision.
	ErrRevisionMismatch = errors.New("forward route revision does not match")
	// ErrRequestConflict: the request id was recorded for another request.
	ErrRequestConflict = errors.New("request_id was used for another request")
	// ErrInvalidRequest: the request itself is malformed (request_id, an
	// id on create, expected_revision missing).
	ErrInvalidRequest = errors.New("invalid forward request")
)
View Source
var DefaultReservedPorts = []uint32{22}

DefaultReservedPorts are reserved on every node, with the node's own service ports (the forward node's port, API and metrics ports, the proxy node's port and protocol ports): SSH.

View Source
var ErrCheckUnavailable = errors.New("the node's Agent cannot run forward checks")

ErrCheckUnavailable: the node's Agent is not connected or does not run the forward checks.

View Source
var ErrDiagnoseBusy = errors.New("too many route diagnoses are running; retry shortly")

ErrDiagnoseBusy refuses a diagnosis while MaxConcurrentDiagnoses run (RESOURCE_EXHAUSTED).

View Source
var ErrInvalidReport = errors.New("invalid forward report")

ErrInvalidReport: a report the kernel cannot store (a counter beyond the signed 64-bit range of its columns).

View Source
var ErrNoState = errors.New("forward node has no stored forwarding state")

ErrNoState: the node has no stored forwarding state (no accepted plan covered it yet).

View Source
var ErrNodeNotFound = errors.New("forward node not found")

ErrNodeNotFound: the node's proxy or forward node row does not exist.

Functions

func NodeConfigMember

func NodeConfigMember(db *gorm.DB, node agentcontrol.AgentNode) (negotiated bool, member map[string]any, err error)

NodeConfigMember answers what a node's desired configuration carries for forwarding (forward-sdk.md section 8.1). negotiated is the node's persisted flag: whether its Agent's last Hello negotiated forward.v1. Every builder of the node's configuration (the Hello reconcile, the periodic refresh, node.sync) decides the format from it, never from a live session, so they all build the same document and the configuration revision does not flap. With negotiated, member is the value of the anixops.nodeconfig/v2 member "forward": the node's stamped NodeForwardState, or a state holding only node_ref (generation 0: Control has no state for the node yet, and the Agent keeps what it runs).

func OnStateChange

func OnStateChange(listener StateListener) (cancel func())

OnStateChange registers listener for every committed generation change, of every Service in the process. The Agent Control stream registers one that pushes the changed nodes' configurations. cancel removes it.

func WritePrometheus

func WritePrometheus(body *strings.Builder, db *gorm.DB)

WritePrometheus renders the forwarding gauges in the Prometheus text format, from db: the nodes behind their desired generation and the largest lag, the hop errors of the nodes' latest reports, and whether the last plan was refused, and the counter of generation recoveries. A database without the tables writes nothing.

Types

type Authorizer

type Authorizer interface {
	AuthorizeCapability(ctx context.Context, host packagebridge.HostIdentity, capability string) error
}

Authorizer admits a calling host to a capability.

type CheckItem

type CheckItem struct {
	// Target is "host:port"; Protocol "tcp" or "udp".
	Target   string `json:"target"`
	Protocol string `json:"protocol"`
	Status   string `json:"status"`
	Code     string `json:"code"`
	Message  string `json:"message"`
	RTTMicro uint32 `json:"rtt_us"`
}

CheckItem is one listener, port claim or upstream a check looked at.

type CheckResult

type CheckResult struct {
	Check      string      `json:"check"`
	RouteID    string      `json:"route_id"`
	HopIndex   uint32      `json:"hop_index"`
	Generation uint64      `json:"generation"`
	Status     string      `json:"status"`
	Code       string      `json:"code"`
	Message    string      `json:"message"`
	Items      []CheckItem `json:"items"`
	// OperationID is the agent.diagnostic operation that carried the
	// check; set by the NodeChecker.
	OperationID string `json:"-"`
}

CheckResult is the structured answer of one forward check: the "result" member of the agent.diagnostic state (sdk/api/agent/v1/PROTOCOL.md, "Forward diagnostic checks").

type Convergence

type Convergence struct {
	NodeRef string
	// Desired is the generation of the node's stored state.
	Desired uint64
	// Reported is false before the node's first report; Applied,
	// Generation and HopErrors are its latest report's.
	Reported   bool
	Generation uint64
	Applied    bool
	HopErrors  int
	ObservedAt time.Time
}

Convergence is one node's desired and reported generation.

func NodeConvergence

func NodeConvergence(ctx context.Context, db *gorm.DB) ([]Convergence, error)

NodeConvergence answers every node with a desired state and its latest report, by node.

func (Convergence) Converged

func (c Convergence) Converged() bool

Converged is true when the node reported that it runs its desired generation.

func (Convergence) Lag

func (c Convergence) Lag() uint64

Lag is how many generations the node runs behind its desired one: the desired generation before its first report.

type DNSRefusedError

type DNSRefusedError struct {
	Violations   []*forwardv1.Violation
	Precondition bool
}

DNSRefusedError is a DNS write refused with violations; Precondition is true when the request is well formed but cannot be applied now.

func (*DNSRefusedError) Error

func (e *DNSRefusedError) Error() string

type EntryHA

type EntryHA struct {
	Service *Service
	// LocalSession reports whether this process holds node's Agent
	// Control session; nil when it holds none.
	LocalSession func(node agentcontrol.AgentNode) bool
	// contains filtered or unexported fields
}

EntryHA is the entry HA controller on one database.

func (*EntryHA) Evaluate

func (e *EntryHA) Evaluate(ctx context.Context) error

Evaluate runs one pass over every binding.

func (*EntryHA) Run

func (e *EntryHA) Run(ctx context.Context)

Run evaluates every DNSEvaluateInterval until ctx ends. Only the singleton-worker lease holder runs it.

type ListOptions

type ListOptions struct {
	// Owner keeps the routes of one owner; empty for every route.
	Owner string
	// NodeRef keeps the routes with a hop on the node.
	NodeRef string
	// PageSize is 100 when 0, at most 1000.
	PageSize uint32
	// PageToken is the next_page_token of the previous page.
	PageToken string
}

ListOptions filter and page ListRoutes.

type NodeChecker

type NodeChecker interface {
	// Vantage answers whether node can run the checks.
	Vantage(node agentcontrol.AgentNode) NodeVantage
	// Check runs one check (service.ForwardDiagnosticChecks) with params
	// and waits for the Agent's answer until ctx ends. An error means the
	// check gave no answer: ErrCheckUnavailable when the node cannot run
	// it, context.DeadlineExceeded when the Agent did not answer in time,
	// anything else when the Agent failed it.
	Check(ctx context.Context, node agentcontrol.AgentNode, action string, params map[string]any) (*CheckResult, error)
}

NodeChecker runs the forward checks of the agent.diagnostic operation on a node's Agent (kernelnodeops.ForwardChecks serves it on the Agent Control streams).

type NodeFilter

type NodeFilter struct {
	// Kind keeps forward or proxy nodes; empty for both.
	Kind string
	// Transport keeps the forward nodes reached so (and proxy nodes, which
	// run an Agent, for AGENT); UNSPECIFIED for every node.
	Transport forwardv1.NodeTransport
	// contains filtered or unexported fields
}

NodeFilter keeps part of the inventory (ListNodes).

type NodeReset

type NodeReset struct {
	NodeRef string `json:"node_ref"`
	// Previous is the stored generation before; Reported the latest
	// report's (0 before the node's first).
	Previous   uint64 `json:"previous_generation"`
	Reported   uint64 `json:"reported_generation"`
	Generation uint64 `json:"generation"`
	StateHash  string `json:"state_hash"`
}

NodeReset is what ResetNode did.

type NodeSettings

type NodeSettings struct {
	PortFirst, PortLast uint32
	ReservedPorts       []uint32
	// Addresses the node is reachable at, primary first: IP addresses only,
	// since the planner admits the previous hop by address.
	Addresses []string
	// Labels such as link=iepl mark trusted links.
	Labels map[string]string
}

NodeSettings are an administrator's forwarding settings for one node. Zero values mean the defaults: DefaultPortFirst to DefaultPortLast, no extra reserved ports, the node row's host when it is an IP address (the proxy node's reported server IP otherwise), no labels.

func SettingsFromProto

func SettingsFromProto(settings *forwardv1.NodeSettings) NodeSettings

SettingsFromProto converts the contract's settings; nil means the defaults.

type NodeStatus

type NodeStatus struct {
	NodeRef      string
	Negotiated   bool
	Capabilities *forwardv1.NodeCapabilities
	ReportedAt   *time.Time
	// Info is the planner's NodeInfo; nil when the node is left out (its
	// row is gone or disabled).
	Info          *forwardv1.NodeInfo
	ReservedPorts []uint32
}

NodeStatus is one inventory node as the kernel sees it.

type NodeVantage

type NodeVantage struct {
	// Connected: the node's Agent holds an Agent Control session.
	Connected bool
	// Capable: the session advertises agent.diagnostic and diag.v1.
	Capable bool
	// Note says why the node cannot probe, for people.
	Note string
}

NodeVantage is whether a node can run the forward checks.

type PlanOutcome

type PlanOutcome struct {
	// Refused is true when a violation refused the plan: nothing changed.
	Refused    bool
	Violations []planner.RouteViolation
	Warnings   []string
	// Changed are the nodes whose desired state moved to a new generation.
	Changed []string
}

PlanOutcome is what one plan did.

type PlanStatus

type PlanStatus struct {
	// Revision counts accepted plans.
	Revision   uint64
	Refused    bool
	Reason     string
	Violations []*forwardv1.Violation
	Warnings   []string
	PlannedAt  *time.Time
}

PlanStatus is the outcome of the last plan.

type RefusedError

type RefusedError struct {
	Violations   []planner.RouteViolation
	Precondition bool
}

RefusedError is a route write refused by validation or planning. Violations carry the route each belongs to; Precondition is true when the nodes cannot host the routes (ports, marks, nodes, capabilities, another route) rather than the route being malformed.

func (*RefusedError) Error

func (e *RefusedError) Error() string

func (*RefusedError) ProtoViolations

func (e *RefusedError) ProtoViolations() []*forwardv1.Violation

ProtoViolations answers the violations in the contract, each with its route.

type ReportResult

type ReportResult struct {
	// Stored is false when a report observed later was stored before.
	Stored bool
	// Metered counts the counters that grew the ledger.
	Metered int
	// Replanned is true when the report's traffic used up a route's quota
	// and a plan paused it.
	Replanned bool
	// Recovered is true when the report was ahead of the node's stored
	// generation and moved it (generation.go).
	Recovered bool
}

ReportResult is what RecordReport did.

type RouteTraffic

type RouteTraffic = model.KernelForwardTraffic

RouteTraffic is one hour of a route hop's metered traffic on a node.

type Server

type Server struct {
	Service    *Service
	Authorizer Authorizer
}

Server serves ForwardControl to official packages: every call is authorized for kernel.forward.v1 against the calling host's current generation, then answered by Service.

func (*Server) For

For returns the ForwardControl server that host reaches; it has the shape of packagebridge.KernelForwardProvider.

type Service

type Service struct {
	DB *gorm.DB
	// Cluster names the Agent identities the planner pins on encrypted
	// links (planner.Options.Cluster); the module runtime's cluster
	// (module_runtime.cluster, "default") when nil.
	Cluster func() string
	// AnixOps answers whether routes may use the experimental AnixOps engine
	// (forward.anixops_experimental); the configuration's value when nil.
	AnixOps func() bool
	// Now defaults to time.Now.
	Now func() time.Time
	// Checks runs the forward checks of agent.diagnostic on the nodes'
	// Agents for DiagnoseRoute; nil (the command line) runs none.
	Checks NodeChecker
	// Probes are DiagnoseRoute's dials from Control; the zero value dials
	// with the system resolver.
	Probes service.DiagnosisProbes
	// DNSKEK answers the key-encryption key that seals DNS provider
	// credentials (L2); module_runtime.ca_kek when nil.
	DNSKEK func() string
	// NewDNSProvider builds a DNS provider client; forwardddns.New when
	// nil.
	NewDNSProvider func(kind string, config, credentials map[string]string) (forwardddns.Provider, error)
}

Service is the kernel's forwarding state on one database. Its methods are safe for concurrent use; every plan serializes on the database's lock row.

func New

func New(db *gorm.DB) *Service

New returns the service on db with the configured cluster.

func (*Service) CreateDNSBinding

func (s *Service) CreateDNSBinding(ctx context.Context, requestID string, binding *forwardv1.DnsBinding) (*forwardv1.DnsBinding, error)

CreateDNSBinding binds a route to a provider zone.

func (*Service) CreateDNSProvider

func (s *Service) CreateDNSProvider(ctx context.Context, requestID string, provider *forwardv1.DnsProvider, credentials map[string]string) (*forwardv1.DnsProvider, error)

CreateDNSProvider stores a provider with its credentials sealed.

func (*Service) CreateForwardNode

func (s *Service) CreateForwardNode(ctx context.Context, requestID string, nodeRecord *forwardv1.ForwardNodeRecord, settings *forwardv1.NodeSettings) (*forwardv1.NodeSummary, error)

CreateForwardNode adds a forward node, as ForwardControl.CreateForwardNode.

func (*Service) CreateRoute

func (s *Service) CreateRoute(ctx context.Context, requestID string, route *forwardv1.Route) (*forwardv1.Route, error)

CreateRoute validates, plans and stores route under a new id, as ForwardControl.CreateRoute. A *RefusedError refuses it.

func (*Service) DeleteDNSBinding

func (s *Service) DeleteDNSBinding(ctx context.Context, requestID string, id uint64, purge bool) error

DeleteDNSBinding removes a binding; with purge it first deletes the records Control published (ErrDNSPurge when the provider fails, and the binding stays).

func (*Service) DeleteDNSProvider

func (s *Service) DeleteDNSProvider(ctx context.Context, requestID string, id uint64) error

DeleteDNSProvider removes a provider no binding uses.

func (*Service) DeleteForwardNode

func (s *Service) DeleteForwardNode(ctx context.Context, requestID string, id uint64) error

DeleteForwardNode removes a forward node, its inventory entry, desired state and report, its credential and its Agent certificates, as ForwardControl.DeleteForwardNode.

func (*Service) DeleteRoute

func (s *Service) DeleteRoute(ctx context.Context, requestID, routeID string) error

DeleteRoute removes a route, as ForwardControl.DeleteRoute: its hops leave every node's state in the next generation, and its ports and marks stay held for ReleaseGrace.

func (*Service) DiagnoseRoute

func (s *Service) DiagnoseRoute(ctx context.Context, routeID string, timeout time.Duration) (*forwardv1.DiagnoseRouteResponse, error)

DiagnoseRoute diagnoses a stored route within timeout (0 for DefaultDiagnoseTimeout, at most MaxDiagnoseTimeout, and never past ctx's deadline less a second). A diagnosis of the same route that finished within DiagnoseCacheTTL, or that is running, is answered (cached). ErrNotFound for an unknown route, ErrDiagnoseBusy when too many run.

func (*Service) Enforcement

func (s *Service) Enforcement(ctx context.Context, routeIDs []string) (map[string]string, error)

Enforcement answers why Control pauses each of routeIDs that it pauses (EnforcedQuota, EnforcedExpired).

func (*Service) GetDNSProvider

func (s *Service) GetDNSProvider(ctx context.Context, id uint64) (*forwardv1.DnsProvider, error)

GetDNSProvider answers one provider without credentials.

func (*Service) GetNode

func (s *Service) GetNode(ctx context.Context, nodeRef string) (*forwardv1.GetNodeResponse, error)

GetNode answers one node with its desired state and latest report, as ForwardControl.GetNode; ErrNodeNotFound when its row does not exist.

func (*Service) GetRoute

func (s *Service) GetRoute(ctx context.Context, routeID string) (*forwardv1.Route, error)

GetRoute answers a stored route; ErrNotFound for an unknown id.

func (*Service) LatestReport

func (s *Service) LatestReport(ctx context.Context, nodeRef string) (*forwardv1.NodeForwardReport, bool, error)

LatestReport answers a node's latest report without its counters; found is false before its first.

func (*Service) ListDNSBindings

func (s *Service) ListDNSBindings(ctx context.Context, routeID string, providerID uint64) ([]*forwardv1.DnsBinding, error)

ListDNSBindings answers the bindings, filtered by route and provider.

func (*Service) ListDNSProviders

func (s *Service) ListDNSProviders(ctx context.Context) ([]*forwardv1.DnsProvider, error)

ListDNSProviders answers every DNS provider without credentials.

func (*Service) ListNodes

func (s *Service) ListNodes(ctx context.Context, filter NodeFilter) ([]*forwardv1.NodeSummary, error)

ListNodes answers the forward nodes and the inventory's proxy nodes, as ForwardControl.ListNodes.

func (*Service) ListRoutes

func (s *Service) ListRoutes(ctx context.Context, opts ListOptions) ([]*forwardv1.Route, string, error)

ListRoutes answers routes by id, a page at a time, and the token of the next page (empty after the last).

func (*Service) Maintain

func (s *Service) Maintain(ctx context.Context) error

Maintain does the forwarding state's timed work: it replans when a route's expiry passed (Control pauses it, forward-sdk.md section 5.3), and forgets request ids after RequestRetention. Allocations whose grace period ended are released by the next plan.

func (*Service) Nodes

func (s *Service) Nodes(ctx context.Context) ([]NodeStatus, error)

Nodes answers the inventory rows with the NodeInfo the planner sees.

func (*Service) PlanRoute

PlanRoute runs the planner for one route without storing anything, as ForwardControl.PlanRoute: request nodes replace the inventory's of the same reference, the route keeps its own allocations when it has an id, and every other allocation (deleted routes in their grace period too) is taken.

func (*Service) PlanStatus

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

PlanStatus answers the outcome of the last plan; the zero status before the first.

func (*Service) RecordHello

func (s *Service) RecordHello(ctx context.Context, node agentcontrol.AgentNode, caps *forwardv1.NodeCapabilities, agentVersion string) (outcome PlanOutcome, replanned bool, err error)

RecordHello records what a node's Agent said in its Hello. caps is nil when the session did not negotiate forward.v1: the node's configuration then falls back to anixops.nodeconfig/v1 and its inventory entry (with its last capabilities) stays. With caps the node joins the inventory, its configuration becomes anixops.nodeconfig/v2, and a change of its capabilities replans. replanned reports whether a plan ran.

func (*Service) RecordReport

func (s *Service) RecordReport(ctx context.Context, node agentcontrol.AgentNode, report *forwardv1.NodeForwardReport, observedAt, receivedAt time.Time) (ReportResult, error)

RecordReport stores node's report (already checked with wire.DecodeReport for the node) as its latest and meters its counters. observedAt is the report's observation time (the receive time when the Agent did not say).

func (*Service) Replan

func (s *Service) Replan(ctx context.Context, reason string) (PlanOutcome, error)

Replan plans every stored route against the current inventory, after an inventory change or on demand. A refused plan changes nothing; it is recorded in the status row and logged.

func (*Service) ResetNode

func (s *Service) ResetNode(ctx context.Context, nodeRef string) (NodeReset, error)

ResetNode moves a node's forwarding generation above both its stored one and the one its latest report holds, keeping its hops, and pushes the node's configuration to its session in this process (another process's sessions get it with their next configuration refresh, within a minute). It is the operator's command (anix-control forward reset-node), never served on ForwardControl. ErrNoState when the node has no stored state.

func (*Service) RouteDNS

func (s *Service) RouteDNS(ctx context.Context, routeID string) (*forwardv1.RouteDnsStatus, error)

RouteDNS answers a route's entry HA status; ErrNotFound for an unknown route.

func (*Service) RouteHealth

func (s *Service) RouteHealth(ctx context.Context, routeID string) ([]*forwardv1.UpstreamHealth, error)

RouteHealth answers the health of every upstream of a route's hops from its nodes' latest reports (ForwardControl.GetRouteHealth). ErrNotFound for an unknown route.

func (*Service) RouteStats

func (s *Service) RouteStats(ctx context.Context, routeID string) ([]*forwardv1.Counters, error)

RouteStats answers a route's metered counters per hop and node, summed over counter epochs (ForwardControl.GetRouteStats): active_conns and observed_at are the latest epoch's, counter_epoch is empty. ErrNotFound for an unknown route.

func (*Service) Run

func (s *Service) Run(ctx context.Context)

Run maintains the forwarding state every MaintenanceInterval until ctx ends. Only the singleton-worker lease holder runs it.

func (*Service) SetNodeSettings

func (s *Service) SetNodeSettings(ctx context.Context, node agentcontrol.AgentNode, settings NodeSettings) (PlanOutcome, error)

SetNodeSettings stores node's forwarding settings, which puts the node in the inventory, and replans. ErrNodeNotFound when the node's row does not exist.

func (*Service) SetNodeSettingsAnswer

func (s *Service) SetNodeSettingsAnswer(ctx context.Context, nodeRef string, settings NodeSettings) (*forwardv1.SetNodeSettingsResponse, error)

SetNodeSettingsAnswer is SetNodeSettings with the contract's answer: the node's summary after the replan, and the violations of a refused replan.

func (*Service) State

func (s *Service) State(ctx context.Context, nodeRef string) (*forwardv1.NodeForwardState, bool, error)

State answers a node's desired state as the last accepted plan stamped it; found is false before the node's first plan.

func (*Service) Traffic

func (s *Service) Traffic(ctx context.Context, routeID string, since, until time.Time) ([]RouteTraffic, error)

Traffic answers the ledger's hourly buckets of a route from since (inclusive) to until (exclusive), by hour, hop and node.

func (*Service) TrafficBuckets

func (s *Service) TrafficBuckets(ctx context.Context, routeID, nodeRef string, since, until time.Time) ([]*forwardv1.TrafficBucket, bool, error)

TrafficBuckets answers the ledger's hourly buckets filtered by route and node from since (inclusive) to until (exclusive), as ForwardControl.GetTraffic; zero times default to the 24 hours up to the end of the current hour.

func (*Service) UpdateDNSBinding

func (s *Service) UpdateDNSBinding(ctx context.Context, requestID string, binding *forwardv1.DnsBinding) (*forwardv1.DnsBinding, error)

UpdateDNSBinding replaces a binding's record types, TTL and paused flag.

func (*Service) UpdateDNSProvider

func (s *Service) UpdateDNSProvider(ctx context.Context, requestID string, provider *forwardv1.DnsProvider, credentials map[string]string) (*forwardv1.DnsProvider, error)

UpdateDNSProvider replaces a provider's name and configuration, and the credentials sent; a credential left out or sent as the placeholder keeps its stored value.

func (*Service) UpdateForwardNode

func (s *Service) UpdateForwardNode(ctx context.Context, requestID string, nodeRecord *forwardv1.ForwardNodeRecord) (*forwardv1.UpdateForwardNodeResponse, error)

UpdateForwardNode replaces a forward node's editable fields, as ForwardControl.UpdateForwardNode.

func (*Service) UpdateRoute

func (s *Service) UpdateRoute(ctx context.Context, requestID string, route *forwardv1.Route, expectedRevision uint64) (*forwardv1.Route, error)

UpdateRoute replaces a stored route at expectedRevision, as ForwardControl.UpdateRoute.

type StateListener

type StateListener func(nodes []agentcontrol.AgentNode)

StateListener receives the nodes whose desired state moved to a new generation, after the plan that moved it committed.

Jump to

Keyboard shortcuts

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