local

package
v0.13.0 Latest Latest
Warning

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

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

Documentation

Overview

Package local is the per-sandbox worker-node local API (ADR-0064): an HTTP-over-UDS surface the runtime shim calls for the synchronous fn-to-fn invoke verb (context.invoke). One listener per sandbox; the caller Ref is fixed at provisioning, so identity is connection-scoped — a request body can never name a different caller. A pool worker's listener serves the pool's members, each request as the member it names in the X-Funcd-Member header. No published SDK — reached only through the built-in shim.

It nests under the blueprint's internal/workernode node-agent component.

Index

Constants

View Source
const (
	DependencyKV     = "kv"
	DependencyBlob   = "blob"
	DependencyLink   = "link"
	DependencySocket = "socket"

	ReasonForbidden          = "Forbidden"
	ReasonNotFound           = "NotFound"
	ReasonNotReady           = "NotReady"
	ReasonStorageUnreachable = "StorageUnreachable"
	ReasonTimeout            = "Timeout"
	ReasonUnreachable        = "Unreachable"
)

The kinds and reasons of a DependencyReport (ADR-0215 Contracts).

View Source
const (
	DependencySQL        = "sql"
	ReasonEngineNotReady = "EngineNotReady"
)

DependencySQL is the dependency-report kind of a spec.sql binding, and ReasonEngineNotReady its reason while the database's engine is not Ready (ADR-0233 Decision 8).

View Source
const DependencyCheckBudget = 50 * time.Millisecond

DependencyCheckBudget bounds one dependency check: half the reconciler's 100 ms readiness probe, and a pool host's bound for all its members (ADR-0215 Decision 4).

View Source
const (
	MaxSQLStatements = 500
)

The SQL request bounds (ADR-0233 Decision 3).

View Source
const MemberHeader = "X-Funcd-Member"

MemberHeader names, on every request to a pool worker's local API, the pool member it is made for.

View Source
const NestedCapOp = "workernode.local.nested-cap"

NestedCapOp is the fault Op of a nested call refused by the per-target in-flight cap (ADR-0147).

Variables

View Source
var ErrEngineNotReady = errors.New("the database's engine is not ready")

ErrEngineNotReady marks a SQL call or check whose database has no Ready engine.

Functions

func CheckDir added in v0.2.1

func CheckDir(dir string) error

CheckDir reports an error when dir is too long to hold the local API sockets. Every socket name has the same length, so a dir that passes holds a socket for any function.

func NewHandler

func NewHandler(caller Ref, res Resolver, inv Invoker, authz auth.Authorizer, kv KV, blob Blob,
	deps DependencyChecker, logger *slog.Logger, opts ...Option) http.Handler

NewHandler builds the per-sandbox local API handler: POST /invoke/{alias} (ADR-0064/0075) plus, when kv is non-nil, the KV verbs GET/PUT/DELETE /kv/{binding}/{key} + list (ADR-0069). caller is the fixed sandbox identity (connection-scoped) — the handler never reads a caller from the request. authz is the PDP (ADR-0075): after the Resolver maps the alias→target (naming; unknown alias ⇒ Forbidden), the handler asks it `link::invoke` on the target Function — a deny (a forbid Policy revoking a declared link) ⇒ Forbidden, before forwarding. The caller principal is built from the fixed Ref, never the request. Every invoke is logged through logger (the broker is the audit point): an allowed call at Info, a denial / upstream error at Warn. A nil logger defaults to slog.Default(). deps non-nil registers GET /health/dependencies (ADR-0215 Decision 3); nil registers no route, so a shim reads 404 as a pass.

func Serve

func Serve(ctx context.Context, path string, h http.Handler, logger *slog.Logger) error

Serve runs h on a Unix domain socket at path (bind-mounted into the sandbox) until ctx is done. A stale socket file at path is removed first. The server's own errors are logged through logger (nil means slog.Default()).

Types

type Blob

type Blob interface {
	Get(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string) ([]byte, bool, error)
	Put(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string, data []byte) error
	Delete(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string) error
	List(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, prefix string) ([]string, error)
	SignedURL(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string, opts blob.SignOptions) (string, error)
}

Blob is the function-facing blob port the worker-node local API routes to — the services/blob.Facade satisfies it. The caller's namespace + function are supplied by the handler (connection-scoped from the sandbox's fixed Ref), NEVER read from the request: a function can only reach its own namespace's blob, and its binding resolution is keyed by its trustworthy fixed function identity (ADR-0127/0073).

type DependencyChecker added in v0.11.0

type DependencyChecker interface {
	Check(ctx context.Context, caller Ref) *DependencyReport
}

DependencyChecker checks the bindings of caller (ADR-0215 Decision 3): nil when all pass, else the first failure.

type DependencyReport added in v0.11.0

type DependencyReport struct {
	Kind    string `json:"kind"`    // kv | blob | sql | link | socket
	Binding string `json:"binding"` // the alias
	Reason  string `json:"reason"`  // Forbidden | NotFound | NotReady | StorageUnreachable | Timeout | Unreachable | EngineNotReady
	Message string `json:"message"`
}

DependencyReport is the first binding of a sandbox's caller that fails its dependency check (ADR-0215 Decision 3), the body of a 503 from GET /health/dependencies and, passed through by the shim, from its /health/readiness.

type ExecResult added in v0.13.0

type ExecResult struct {
	RowsAffected int64               `json:"rowsAffected"`
	LastInsertID int64               `json:"lastInsertId"`
	Columns      []string            `json:"columns,omitempty"`
	Values       [][]json.RawMessage `json:"values,omitempty"`
}

ExecResult is one execute statement's outcome; a RETURNING statement also carries its rows.

type FunctionStore

type FunctionStore interface {
	Get(ctx context.Context, gvk v1.GroupVersionKind, ns v1.NamespaceName, name v1.ObjectName) (v1.Object, error)
}

FunctionStore is the read-only store access the resolver needs (a subset of store.Store).

type HandlerHolder

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

HandlerHolder is a settable http.Handler indirection. The local API Invoker is built from it BEFORE the data-plane handler exists (the reconciler is constructed before the data plane, since the data plane wraps the reconciler's activator/Endpoints); Set is called once the data plane is built. Until then it answers 503.

func (*HandlerHolder) ServeHTTP

func (hh *HandlerHolder) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP delegates to the installed handler, or 503 until one is set.

func (*HandlerHolder) Set

func (hh *HandlerHolder) Set(h http.Handler)

Set installs the real target handler.

type Invoker

type Invoker interface {
	Invoke(ctx context.Context, target Ref, input []byte, timeout time.Duration) ([]byte, error)
}

Invoker performs the synchronous call to target and returns its output bytes. On a target non-2xx (e.g. the shim's 422 input / 500 output contract failures, ADR-0058) it returns an *UpstreamError carrying that exact status+body to propagate verbatim; on a transport/cold-wake-timeout failure it returns a fault error (e.g. fault.Unavailable → 503).

func NewInvoker

func NewInvoker(dataPlane http.Handler) Invoker

NewInvoker returns an Invoker that forwards through the given in-process data-plane handler.

func NewNestedCapInvoker added in v0.4.0

func NewNestedCapInvoker(inner Invoker, maxPerTarget int, meter metric.Meter) Invoker

NewNestedCapInvoker wraps inner with the per-target nested-call in-flight cap (ADR-0147): a call is counted from before the data-plane hand-off until it returns (a cold wake included). With maxPerTarget (≥ 1) calls to the target in flight, the next is refused at once without calling inner: fault.ResourceExhausted (429, no Retry-After: a slot frees on return, not on a clock), Op NestedCapOp, and the Int64Counter "funcd.invoke.nested.refused" +1 with the target's namespace and function.

type KV

type KV interface {
	Get(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string) ([]byte, bool, error)
	Put(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string, value []byte) error
	Delete(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, key string) error
	List(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding, prefix string) ([]string, error)
}

KV is the function-facing KV port the worker-node local API routes to — the services/kv.Facade satisfies it. The caller's namespace + function are supplied by the handler (connection-scoped from the sandbox's fixed Ref), NEVER read from the request: a function can only reach its own namespace's KV, and its binding resolution is keyed by its trustworthy fixed function identity (ADR-0073).

type Manager

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

Manager provisions one per-function worker-node local API listener (ADR-0064), lazily and idempotently: SocketFor(ns, name) ensures a UDS listener for that caller is serving and returns its path — the reconciler sets it as FUNCD_INVOKE_SOCKET so the caller's shim can dial context.invoke. The caller Ref is fixed per listener (connection-scoped identity); a pool worker's listener (PoolSocketFor) serves each request as the pool member it names in MemberHeader. Close() stops every listener. This is the same-node (process/containerd) provisioning; the V2 multi-node lattice is a transport swap behind the Invoker.

func NewManager

func NewManager(dir string, store FunctionStore, invoker Invoker, authz auth.Authorizer, kv KV, blob Blob,
	deps DependencyChecker, logger *slog.Logger, opts ...Option) *Manager

NewManager builds a Manager serving sockets under dir. invoker is the (possibly late-bound) data-plane forwarder; store backs link resolution; authz (nil-able) is the invoke PDP (ADR-0075) the per-sandbox handler asks link::invoke; kv (nil-able) is the function-facing KV port (ADR-0069) the per-sandbox handler routes /kv/… to; blob (nil-able) is the function-facing blob port (ADR-0127) the handler routes /blob/… to; deps (nil-able) answers GET /health/dependencies (ADR-0215).

func (*Manager) Close

func (m *Manager) Close()

Close stops every serving listener and returns once each is closed. SocketFor fails after Close.

func (*Manager) PoolSocketFor added in v0.5.0

func (m *Manager) PoolSocketFor(ns v1.NamespaceName, pool v1.ObjectName, members []v1.ObjectName) (string, error)

PoolSocketFor ensures the local API of the pool worker named pool is serving and returns its socket path, which every member's process shares. A request naming one of members in MemberHeader is served as that member's Ref, as its own SocketFor listener would serve it (invoke, KV, blob); any other request, one with no MemberHeader included, is refused 403 before any port is called. A repeated call keeps the path and replaces the member set, as members join and leave the pool. Remove(ns, pool) stops it.

func (*Manager) Remove

func (m *Manager) Remove(ns v1.NamespaceName, name v1.ObjectName)

Remove stops + deletes the local API listener for (ns, name), a Function's or a pool worker's, if any — called when the Function or the pool is deleted, so the socket lifecycle tracks the resource (controller-driven, not leaked). It returns once the listener is closed: closing a Unix listener unlinks its path, which a Function re-created under the same name binds again (issue #491). It holds mu until then, so a concurrent SocketFor binds after it.

func (*Manager) SocketFor

func (m *Manager) SocketFor(ns v1.NamespaceName, name v1.ObjectName) (string, error)

SocketFor ensures the local API for (ns, name) is serving and returns its socket path. Idempotent.

type Option added in v0.13.0

type Option func(*options)

Option adds an optional port to the local API.

func WithSQL added in v0.13.0

func WithSQL(s SQL) Option

WithSQL routes /sql/{binding}/query and /execute to s (ADR-0233); without it there are no /sql routes.

type QueryResult added in v0.13.0

type QueryResult struct {
	Columns []string            `json:"columns"`
	Values  [][]json.RawMessage `json:"values"`
}

QueryResult is one query statement's rows in rqlite's array format, which keeps duplicate column names.

type Ref

type Ref struct {
	Namespace v1.NamespaceName
	Function  v1.ObjectName
}

Ref identifies a function replica's caller identity (namespace + function).

func (Ref) String

func (r Ref) String() string

String renders the ref as "<namespace>/<function>" for logs.

type Resolver

type Resolver interface {
	Resolve(ctx context.Context, caller Ref, alias string) (target Ref, timeout time.Duration, err error)
}

Resolver maps (caller, alias) → the link target, applying the link-as-grant rule (default-deny): fault.Forbidden when the caller declares no such link. Same-namespace only (V1.1).

func NewResolver

func NewResolver(s FunctionStore) Resolver

NewResolver returns the store-backed link Resolver.

type SQL added in v0.13.0

type SQL interface {
	Query(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding string, stmts []json.RawMessage) ([]QueryResult, error)
	Execute(ctx context.Context, ns v1.NamespaceName, fn v1.ObjectName, binding string, stmts []json.RawMessage) ([]ExecResult, error)
}

SQL is the function-facing SQL port the local API routes to; the services/sql.Facade satisfies it. As for KV, the caller's namespace and function come from the sandbox's fixed Ref, never from the request (ADR-0233).

type UpstreamError

type UpstreamError struct {
	Status int
	Body   []byte
}

UpstreamError carries a target's non-2xx response so the local API propagates it verbatim — the daemon does NOT re-validate the contract (the target's shim already did, ADR-0064).

func (*UpstreamError) Error

func (e *UpstreamError) Error() string

Jump to

Keyboard shortcuts

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