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
- Variables
- func NodeConfigMember(db *gorm.DB, node agentcontrol.AgentNode) (negotiated bool, member map[string]any, err error)
- func OnStateChange(listener StateListener) (cancel func())
- func WritePrometheus(body *strings.Builder, db *gorm.DB)
- type Authorizer
- type CheckItem
- type CheckResult
- type Convergence
- type DNSRefusedError
- type EntryHA
- type ListOptions
- type NodeChecker
- type NodeFilter
- type NodeReset
- type NodeSettings
- type NodeStatus
- type NodeVantage
- type PlanOutcome
- type PlanStatus
- type RefusedError
- type ReportResult
- type RouteTraffic
- type Server
- type Service
- func (s *Service) CreateDNSBinding(ctx context.Context, requestID string, binding *forwardv1.DnsBinding) (*forwardv1.DnsBinding, error)
- func (s *Service) CreateDNSProvider(ctx context.Context, requestID string, provider *forwardv1.DnsProvider, ...) (*forwardv1.DnsProvider, error)
- func (s *Service) CreateForwardNode(ctx context.Context, requestID string, nodeRecord *forwardv1.ForwardNodeRecord, ...) (*forwardv1.NodeSummary, error)
- func (s *Service) CreateRoute(ctx context.Context, requestID string, route *forwardv1.Route) (*forwardv1.Route, error)
- func (s *Service) DeleteDNSBinding(ctx context.Context, requestID string, id uint64, purge bool) error
- func (s *Service) DeleteDNSProvider(ctx context.Context, requestID string, id uint64) error
- func (s *Service) DeleteForwardNode(ctx context.Context, requestID string, id uint64) error
- func (s *Service) DeleteRoute(ctx context.Context, requestID, routeID string) error
- func (s *Service) DiagnoseRoute(ctx context.Context, routeID string, timeout time.Duration) (*forwardv1.DiagnoseRouteResponse, error)
- func (s *Service) Enforcement(ctx context.Context, routeIDs []string) (map[string]string, error)
- func (s *Service) GetDNSProvider(ctx context.Context, id uint64) (*forwardv1.DnsProvider, error)
- func (s *Service) GetNode(ctx context.Context, nodeRef string) (*forwardv1.GetNodeResponse, error)
- func (s *Service) GetRoute(ctx context.Context, routeID string) (*forwardv1.Route, error)
- func (s *Service) LatestReport(ctx context.Context, nodeRef string) (*forwardv1.NodeForwardReport, bool, error)
- func (s *Service) ListDNSBindings(ctx context.Context, routeID string, providerID uint64) ([]*forwardv1.DnsBinding, error)
- func (s *Service) ListDNSProviders(ctx context.Context) ([]*forwardv1.DnsProvider, error)
- func (s *Service) ListNodes(ctx context.Context, filter NodeFilter) ([]*forwardv1.NodeSummary, error)
- func (s *Service) ListRoutes(ctx context.Context, opts ListOptions) ([]*forwardv1.Route, string, error)
- func (s *Service) Maintain(ctx context.Context) error
- func (s *Service) Nodes(ctx context.Context) ([]NodeStatus, error)
- func (s *Service) PlanRoute(ctx context.Context, request *forwardv1.PlanRouteRequest) (*forwardv1.PlanRouteResponse, error)
- func (s *Service) PlanStatus(ctx context.Context) (PlanStatus, error)
- func (s *Service) RecordHello(ctx context.Context, node agentcontrol.AgentNode, ...) (outcome PlanOutcome, replanned bool, err error)
- func (s *Service) RecordReport(ctx context.Context, node agentcontrol.AgentNode, ...) (ReportResult, error)
- func (s *Service) Replan(ctx context.Context, reason string) (PlanOutcome, error)
- func (s *Service) ResetNode(ctx context.Context, nodeRef string) (NodeReset, error)
- func (s *Service) RouteDNS(ctx context.Context, routeID string) (*forwardv1.RouteDnsStatus, error)
- func (s *Service) RouteHealth(ctx context.Context, routeID string) ([]*forwardv1.UpstreamHealth, error)
- func (s *Service) RouteStats(ctx context.Context, routeID string) ([]*forwardv1.Counters, error)
- func (s *Service) Run(ctx context.Context)
- func (s *Service) SetNodeSettings(ctx context.Context, node agentcontrol.AgentNode, settings NodeSettings) (PlanOutcome, error)
- func (s *Service) SetNodeSettingsAnswer(ctx context.Context, nodeRef string, settings NodeSettings) (*forwardv1.SetNodeSettingsResponse, error)
- func (s *Service) State(ctx context.Context, nodeRef string) (*forwardv1.NodeForwardState, bool, error)
- func (s *Service) Traffic(ctx context.Context, routeID string, since, until time.Time) ([]RouteTraffic, error)
- func (s *Service) TrafficBuckets(ctx context.Context, routeID, nodeRef string, since, until time.Time) ([]*forwardv1.TrafficBucket, bool, error)
- func (s *Service) UpdateDNSBinding(ctx context.Context, requestID string, binding *forwardv1.DnsBinding) (*forwardv1.DnsBinding, error)
- func (s *Service) UpdateDNSProvider(ctx context.Context, requestID string, provider *forwardv1.DnsProvider, ...) (*forwardv1.DnsProvider, error)
- func (s *Service) UpdateForwardNode(ctx context.Context, requestID string, nodeRecord *forwardv1.ForwardNodeRecord) (*forwardv1.UpdateForwardNodeResponse, error)
- func (s *Service) UpdateRoute(ctx context.Context, requestID string, route *forwardv1.Route, ...) (*forwardv1.Route, error)
- type StateListener
Constants ¶
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.
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" 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).
const ( CheckOK = "ok" CheckFailed = "failed" CheckInconclusive = "inconclusive" CheckSkipped = "skipped" )
Forward check statuses, as the Agent answers them (CheckResult.Status, CheckItem.Status).
const ( DNSDefaultTTL = 60 DNSMaxTTL = 86400 )
Binding defaults and bounds.
const ( DNSModeDDNS = "ddns" DNSModeCNAME = "cname" )
Binding modes and record types as stored.
const ( 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.
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.
const ( RecoveryReport = "report" RecoveryPlan = "plan" RecoveryOperator = "operator" )
Recovery reasons (anixops_forward_generation_recoveries_total).
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 )
const ( // MaxTrafficWindow bounds GetTraffic's window. MaxTrafficWindow = 31 * 24 * time.Hour // MaxTrafficBuckets bounds one GetTraffic answer. MaxTrafficBuckets = 20000 )
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 )
const ( EnforcedQuota = "quota" EnforcedExpired = "expired" )
Enforcement reasons (KernelForwardRoute.Enforced).
const MaintenanceInterval = time.Minute
MaintenanceInterval is how often the singleton worker maintains the forwarding state.
Variables ¶
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") )
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") )
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.
ErrCheckUnavailable: the node's Agent is not connected or does not run the forward checks.
var ErrDiagnoseBusy = errors.New("too many route diagnoses are running; retry shortly")
ErrDiagnoseBusy refuses a diagnosis while MaxConcurrentDiagnoses run (RESOURCE_EXHAUSTED).
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).
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).
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 ¶
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 ¶
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 ¶
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.
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 ¶
func (s *Server) For(host packagebridge.HostIdentity) forwardv1.ForwardControlServer
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 (*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 ¶
DeleteDNSProvider removes a provider no binding uses.
func (*Service) DeleteForwardNode ¶
DeleteForwardNode removes a forward node, its inventory entry, desired state and report, its credential and its Agent certificates, as ForwardControl.DeleteForwardNode.
func (*Service) DeleteRoute ¶
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 ¶
Enforcement answers why Control pauses each of routeIDs that it pauses (EnforcedQuota, EnforcedExpired).
func (*Service) GetDNSProvider ¶
GetDNSProvider answers one provider without credentials.
func (*Service) GetNode ¶
GetNode answers one node with its desired state and latest report, as ForwardControl.GetNode; ErrNodeNotFound when its row does not exist.
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 ¶
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 ¶
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 ¶
func (s *Service) PlanRoute(ctx context.Context, request *forwardv1.PlanRouteRequest) (*forwardv1.PlanRouteResponse, error)
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
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.