Documentation
¶
Overview ¶
Package agent is the node-side runtime that keeps a node in sync with the control plane. It maintains a single AgentSync bidi stream: a hello on every (re)connect, periodic heartbeats carrying live host metrics, and applies the full AssignmentSet the control plane pushes. Reconciling those assignments against the local container runtime is the executor's job (T-15); this skeleton establishes the stream, heartbeats, and reconnection.
Index ¶
- func GatewayIP(cidr string) (string, error)
- func NetworkName(projectID, envID string) string
- func NextFreeSubnet(used []string) (string, error)
- type Agent
- type BuildServer
- type BuildServerConfig
- type Config
- type Conn
- type ExecServer
- type Executor
- type ExecutorConfig
- type HTTPSourceFetcher
- type HealthConfig
- type HostSample
- type LocalServer
- func (s *LocalServer) CancelBuild(ctx context.Context, req *clusterv1.CancelBuildRequest) (*clusterv1.CancelBuildResponse, error)
- func (s *LocalServer) Exec(...) error
- func (s *LocalServer) ProxyTCP(stream grpc.BidiStreamingServer[clusterv1.TCPChunk, clusterv1.TCPChunk]) error
- func (s *LocalServer) QueryLogs(q *zatterav1.LogQuery, stream grpc.ServerStreamingServer[zatterav1.LogLine]) error
- func (s *LocalServer) RunBuild(req *clusterv1.RunBuildRequest, ...) error
- func (s *LocalServer) Top(ctx context.Context, req *clusterv1.AgentTopRequest) (*clusterv1.AgentTopResponse, error)
- type LogCapture
- type LogServer
- type Manager
- type ManagerConfig
- type Monitor
- type NetScope
- type Probe
- type SampleFunc
- type SourceFetcher
- type StatusSink
- type StreamResolver
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GatewayIP ¶
GatewayIP returns the gateway (.1) address of a /24 subnet — where the per-network internal DNS resolver binds (T-47).
func NetworkName ¶
NetworkName is the node-local Docker network name for an environment. It is deterministic (idempotent EnsureNetwork) and valid per Docker's naming rules.
func NextFreeSubnet ¶
NextFreeSubnet returns the lowest 10.201.X.0/24 not present in used, or an error when the pool is exhausted.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent maintains the node's AgentSync stream to the control plane.
type BuildServer ¶
type BuildServer struct {
clusterv1.UnimplementedAgentLocalServiceServer
// contains filtered or unexported fields
}
BuildServer implements the build methods of AgentLocalService on builder nodes (T-35). Other AgentLocalService methods are added by later tasks (T-41 logs, T-49 exec, T-65 volumes) via the same embedded server.
func NewBuildServer ¶
func NewBuildServer(cfg BuildServerConfig) *BuildServer
NewBuildServer constructs the build server.
func (*BuildServer) CancelBuild ¶
func (s *BuildServer) CancelBuild(_ context.Context, req *clusterv1.CancelBuildRequest) (*clusterv1.CancelBuildResponse, error)
CancelBuild aborts an in-flight build on this node.
func (*BuildServer) RunBuild ¶
func (s *BuildServer) RunBuild(req *clusterv1.RunBuildRequest, stream grpc.ServerStreamingServer[clusterv1.BuildEvent]) error
RunBuild fetches the source, builds it (Dockerfile or nixpacks), pushes the image to the registry, and streams progress back to the control plane.
type BuildServerConfig ¶
type BuildServerConfig struct {
Builder *builder.Builder
Fetch SourceFetcher
RegistryAuth builder.RegistryAuth
RegistryInsecure bool
// LocalLoad loads the built image into the local Docker store instead of
// pushing to a registry (single-node/dev, T-54).
LocalLoad bool
Logger *slog.Logger
}
BuildServerConfig configures the build server.
type Config ¶
type Config struct {
NodeID string
Version string
Clock clock.Clock
Logger *slog.Logger
// Runtime is reconciled against pushed assignments by the executor (T-15).
// When nil the agent maintains the stream/heartbeats but runs no executor
// (e.g. a control-only node without Docker).
Runtime runtime.ContainerRuntime
// HostIP is where the executor publishes container ports (mesh IP, or
// 127.0.0.1 in single-node/dev).
HostIP string
// RegistryAuth pulls private images (join credential, T-17); nil for public.
RegistryAuth *runtime.RegistryAuth
// Dial opens a fresh connection to a control node's AgentSyncService. The
// agent calls it on every (re)connect and closes the returned Conn when the
// session ends.
Dial func(ctx context.Context) (*Conn, error)
// Sample returns host metrics for heartbeats. Defaults to a gopsutil probe.
Sample SampleFunc
// DiskPath is the filesystem the default sampler probes for disk usage.
DiskPath string
// HeartbeatInterval overrides the 10s default (tests shorten it via a fake
// clock).
HeartbeatInterval time.Duration
// OnAssignments is invoked with every AssignmentSet the control plane
// pushes. The executor (T-15) reconciles Docker to it; the skeleton records
// the version and forwards the set here.
OnAssignments func(*clusterv1.AssignmentSet)
}
Config configures an Agent.
type Conn ¶
type Conn struct {
grpc.ClientConnInterface
Close func() error
}
Conn is a control-plane connection paired with a closer. The agent redials on every (re)connect and closes the connection when a session ends.
type ExecServer ¶
type ExecServer struct {
clusterv1.UnimplementedAgentLocalServiceServer
// contains filtered or unexported fields
}
ExecServer implements the interactive methods of AgentLocalService (T-49): Exec (bidi TTY), Top (process snapshot) and ProxyTCP (port-forward leg). It runs on every node against the local container runtime; the control plane's ExecService relays user streams to it over the mesh.
It embeds UnimplementedAgentLocalServiceServer so it can stand alone in tests; the production node composes these methods with the build/log/volume methods into one AgentLocalService when the :8444 mTLS listener is wired.
func NewExecServer ¶
func NewExecServer(rt crt.ContainerRuntime, log *slog.Logger) *ExecServer
NewExecServer builds the exec server over a container runtime.
func (*ExecServer) Exec ¶
func (s *ExecServer) Exec(stream grpc.BidiStreamingServer[clusterv1.AgentExecInput, clusterv1.AgentExecOutput]) error
Exec runs a command inside a running container and relays its TTY/pipes over the bidi stream. The first client message must carry AgentExecStart.
func (*ExecServer) ProxyTCP ¶
func (s *ExecServer) ProxyTCP(stream grpc.BidiStreamingServer[clusterv1.TCPChunk, clusterv1.TCPChunk]) error
ProxyTCP splices the bidi stream to a TCP target. The first chunk carries the dial address ("containerIP:port" or "meshIP:port"); its data (if any) is forwarded after the dial.
func (*ExecServer) Top ¶
func (s *ExecServer) Top(ctx context.Context, req *clusterv1.AgentTopRequest) (*clusterv1.AgentTopResponse, error)
Top returns a one-shot process listing from inside the container.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor converges the local container runtime to the assignment set the control plane pushes. It is the agent's reconcile loop: idempotent, crash-safe (adopts existing containers by label on restart), and never touches containers it does not own.
func NewExecutor ¶
func NewExecutor(cfg ExecutorConfig) *Executor
NewExecutor builds an Executor, filling defaults.
func (*Executor) MatchingStreams ¶
func (e *Executor) MatchingStreams(sel *zatterav1.LogSelector) []logstore.StreamID
MatchingStreams returns the assignment ids on this node whose metadata match the log selector (used by the agent log query server, T-54).
func (*Executor) NetworkScopes ¶
NetworkScopes returns the distinct (project, env) bridge scopes this node currently has assignments for — one per bridge gateway. It is the input to the internal DNS resolver's Reconcile (F26): the resolver serves <svc>.internal on each gateway within that gateway's project+env scope.
func (*Executor) Run ¶
Run drives reconciliation: on every submitted set and on a periodic tick (which retries failed assignments and re-checks liveness) until ctx ends.
func (*Executor) Submit ¶
func (e *Executor) Submit(set *clusterv1.AssignmentSet)
Submit hands the executor the latest assignment set (coalescing: only the newest pending set is kept) and wakes the Run loop.
type ExecutorConfig ¶
type ExecutorConfig struct {
NodeID string
// HostIP is where published ports bind (the node's mesh IP, or 127.0.0.1 in
// single-node/dev).
HostIP string
Runtime runtime.ContainerRuntime
Clock clock.Clock
Logger *slog.Logger
// Report emits status transitions. Required in production; tests capture it.
Report StatusSink
// RegistryAuth pulls private images (from the join credential, T-17). May be
// nil for public images.
RegistryAuth *runtime.RegistryAuth
// PollInterval drives the liveness re-check + retry tick (default 5s).
PollInterval time.Duration
}
ExecutorConfig configures the reconciler.
type HTTPSourceFetcher ¶
HTTPSourceFetcher fetches source tarballs over HTTP(S) with the node's mTLS client (the control blob endpoint is node-cert-authenticated).
func (HTTPSourceFetcher) Fetch ¶
func (h HTTPSourceFetcher) Fetch(ctx context.Context, url string) (io.ReadCloser, error)
Fetch GETs url and returns the body stream.
type HealthConfig ¶
type HealthConfig struct {
Interval time.Duration
Timeout time.Duration
Grace time.Duration
Threshold int
}
HealthConfig is the resolved probe schedule for one instance.
type HostSample ¶
type HostSample struct {
CPUPercent float64
MemoryUsedBytes uint64
MemoryTotalBytes uint64
DiskUsedBytes uint64
DiskTotalBytes uint64
}
HostSample is a point-in-time snapshot of node resource usage carried by a heartbeat.
type LocalServer ¶
type LocalServer struct {
clusterv1.UnimplementedAgentLocalServiceServer
// contains filtered or unexported fields
}
LocalServer composes the node-local AgentLocalService methods that the control plane dials over the mesh (T-54): builds (BuildServer) and interactive exec/top/port-forward (ExecServer). QueryLogs/Stats/volume methods remain Unimplemented until their agent-side backends land.
A single gRPC server can register only one AgentLocalServiceServer, so this type forwards each method to the right sub-server instead of embedding both.
func NewLocalServer ¶
func NewLocalServer(build *BuildServer, exec *ExecServer, logs *LogServer) *LocalServer
NewLocalServer builds the composite. Any sub-server may be nil (e.g. a non-builder node passes build=nil); the corresponding methods then report Unimplemented via the embedded base.
func (*LocalServer) CancelBuild ¶
func (s *LocalServer) CancelBuild(ctx context.Context, req *clusterv1.CancelBuildRequest) (*clusterv1.CancelBuildResponse, error)
CancelBuild dispatches to the build sub-server.
func (*LocalServer) Exec ¶
func (s *LocalServer) Exec(stream grpc.BidiStreamingServer[clusterv1.AgentExecInput, clusterv1.AgentExecOutput]) error
Exec dispatches to the exec sub-server.
func (*LocalServer) ProxyTCP ¶
func (s *LocalServer) ProxyTCP(stream grpc.BidiStreamingServer[clusterv1.TCPChunk, clusterv1.TCPChunk]) error
ProxyTCP dispatches to the exec sub-server.
func (*LocalServer) QueryLogs ¶
func (s *LocalServer) QueryLogs(q *zatterav1.LogQuery, stream grpc.ServerStreamingServer[zatterav1.LogLine]) error
QueryLogs dispatches to the log sub-server.
func (*LocalServer) RunBuild ¶
func (s *LocalServer) RunBuild(req *clusterv1.RunBuildRequest, stream grpc.ServerStreamingServer[clusterv1.BuildEvent]) error
RunBuild dispatches to the build sub-server.
func (*LocalServer) Top ¶
func (s *LocalServer) Top(ctx context.Context, req *clusterv1.AgentTopRequest) (*clusterv1.AgentTopResponse, error)
Top dispatches to the exec sub-server.
type LogCapture ¶
type LogCapture struct {
// contains filtered or unexported fields
}
LogCapture follows the logs of every managed service container and appends them to the node-local logstore keyed by assignment id (T-54). The control plane reads them back via AgentLocalService.QueryLogs.
func NewLogCapture ¶
func NewLogCapture(rt runtime.ContainerRuntime, store *logstore.Segmented, log *slog.Logger) *LogCapture
NewLogCapture builds a capture over a runtime + logstore.
func (*LogCapture) Run ¶
func (c *LogCapture) Run(ctx context.Context)
Run polls the owned service containers and keeps one follower per assignment, starting followers for new containers and stopping them for gone ones.
type LogServer ¶
type LogServer struct {
// contains filtered or unexported fields
}
LogServer implements AgentLocalService.QueryLogs from the node-local logstore (T-54). The control plane's LogService fans this out across nodes and merges.
func NewLogServer ¶
func NewLogServer(store *logstore.Segmented, resolve StreamResolver) *LogServer
NewLogServer builds the agent log query server.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns one Monitor per running assignment. The executor calls Ensure when a container reaches RUNNING and Remove when it is torn down.
func NewManager ¶
func NewManager(base context.Context, cfg ManagerConfig) *Manager
NewManager builds a health manager. base bounds every monitor's lifetime.
func (*Manager) Ensure ¶
func (m *Manager) Ensure(ctx context.Context, a *zatterav1.Assignment, spec *zatterav1.ServiceSpec, containerID string)
Ensure starts a monitor for the assignment if one is not already running (idempotent, so callers may invoke it on every reconcile). A service without a health check is reported HEALTHY immediately. For HTTP/TCP probes the container is inspected to resolve the probe address; the inspect happens only when a monitor is actually created.
type ManagerConfig ¶
type ManagerConfig struct {
Clock clock.Clock
Report StatusSink
Runtime crt.ContainerRuntime
HostIP string
Logger *slog.Logger
}
ManagerConfig configures the health manager.
type Monitor ¶
type Monitor struct {
// contains filtered or unexported fields
}
Monitor probes one instance on the clock and reports state transitions (only on change) via the sink.
type NetScope ¶
NetScope is one (project, env) bridge network this node runs containers on, with the gateway IP where the per-network internal DNS resolver binds.
type SampleFunc ¶
type SampleFunc func() HostSample
SampleFunc returns the current host sample. Production uses gopsutil; tests inject a deterministic stub.
type SourceFetcher ¶
SourceFetcher fetches a source tarball from a control-plane blob URL. The production implementation dials over node mTLS; tests inject a fake.
type StatusSink ¶
type StatusSink func(observed map[string]*zatterav1.AssignmentObserved)
StatusSink receives observed instance-status transitions. The agent wires it to enqueue a StatusBatch on the sync stream.
type StreamResolver ¶
type StreamResolver func(sel *zatterav1.LogSelector) []logstore.StreamID
StreamResolver maps a log selector to the node-local stream ids (assignment ids) it should read, using the metadata the agent already holds.