agentwire

package
v0.0.0-...-5b32b38 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package agentwire defines the wire protocol of the agent channel: the ADR-041 observation frames (v1) and the ADR-052 typed command frames (v2). Both sides — the agent (internal/waker) and the control plane (internal/handlers, internal/dockerruntime) — share these types, so the vocabulary is defined exactly once and every command on the wire is one of the enumerated methods below, never an opaque byte stream to the daemon.

Index

Constants

View Source
const (
	MethodContainerCreate      = "ContainerCreate"
	MethodContainerStart       = "ContainerStart"
	MethodContainerStop        = "ContainerStop"
	MethodContainerRestart     = "ContainerRestart"
	MethodContainerRename      = "ContainerRename"
	MethodContainerRemove      = "ContainerRemove"
	MethodContainerInspect     = "ContainerInspect"
	MethodContainerWait        = "ContainerWait"
	MethodContainerList        = "ContainerList"
	MethodContainerLogs        = "ContainerLogs" // stream
	MethodContainerStats       = "ContainerStats"
	MethodContainersPrune      = "ContainersPrune"
	MethodContainerExecCreate  = "ContainerExecCreate"
	MethodContainerExecStart   = "ContainerExecStart"
	MethodContainerExecInspect = "ContainerExecInspect"
	MethodContainerExecResize  = "ContainerExecResize"
	MethodImagePull            = "ImagePull" // stream
	MethodImagePush            = "ImagePush" // stream
	MethodImageTag             = "ImageTag"
	MethodImageInspect         = "ImageInspect"
	MethodImageList            = "ImageList"
	MethodImageRemove          = "ImageRemove"
	MethodImagesPrune          = "ImagesPrune"
	MethodVolumeCreate         = "VolumeCreate"
	MethodVolumeInspect        = "VolumeInspect"
	MethodVolumeList           = "VolumeList"
	MethodVolumeRemove         = "VolumeRemove"
	MethodVolumesPrune         = "VolumesPrune"
	MethodNetworkCreate        = "NetworkCreate"
	MethodNetworkConnect       = "NetworkConnect"
	MethodNetworkDisconnect    = "NetworkDisconnect"
	MethodNetworkInspect       = "NetworkInspect"
	MethodNetworkList          = "NetworkList"
	MethodNetworkRemove        = "NetworkRemove"
	MethodNetworksPrune        = "NetworksPrune"
	MethodEvents               = "Events" // stream
	MethodInfo                 = "Info"
	MethodServerVersion        = "ServerVersion"
	MethodDiskUsage            = "DiskUsage"
	MethodRegistryLogin        = "RegistryLogin"
	MethodPing                 = "Ping"
)
View Source
const (
	MethodFileWrite  = "FileWrite"
	MethodFileRead   = "FileRead"
	MethodFileRemove = "FileRemove"
	MethodFileStat   = "FileStat"
	MethodFileChown  = "FileChown"
	MethodFileCopy   = "FileCopy"
	MethodDirEnsure  = "DirEnsure"
)

Host-ops vocabulary (ADR-054): file primitives the agent executes in pure Go against the bind-mounted /var/lib/akerdock tree — the helper image is distroless, so there is no shell to fall back on, and every path is validated against that root before it is touched.

View Source
const (
	MethodExecToFile = "ExecToFile"
	MethodFileToExec = "FileToExec"
	MethodFileToURL  = "FileToURL"
	MethodURLToFile  = "URLToFile"
	MethodFileHash   = "FileHash"
)

Pipe vocabulary (ADR-054 tranche C): bulk transfers the agent executes LOCALLY — container exec ↔ host file with compression, host file ↔ presigned URL — so a multi-gigabyte dump never crosses the control plane. Each is a single long-running unary command; only the typed verdict (exit code, size, digest, output tail) travels back.

View Source
const (
	SubprotocolV1 = "akerdock-agent-v1"
	SubprotocolV2 = "akerdock-agent-v2"
	// SubprotocolRelay is the worker→api bridge (ADR-052 §8): a process that
	// does not terminate agent WebSockets sends its typed commands here, and
	// the api forwards them onto the target server's live channel.
	SubprotocolRelay = "akerdock-relay-v1"
)

Channel subprotocols. The agent offers both; the control plane picks v2 when it speaks commands, and an older side falls back to v1 (observations only) — the rail upgrades without a flag day.

View Source
const (
	// FrameObservations carries an acked observation batch (agent → CP).
	FrameObservations = "observations"
	// FrameAck acknowledges an observation batch by sequence (CP → agent).
	FrameAck = "ack"
	// FrameCommand carries one typed command (CP → agent, v2).
	FrameCommand = "cmd"
	// FrameResult answers a command by id (agent → CP, v2).
	FrameResult = "res"
	// FrameStream carries one chunk of a command's output stream — logs
	// follow, pull/push progress, daemon events (agent → CP, v2).
	FrameStream = "stream"
	// FrameCancel aborts a command in flight and closes its stream
	// (CP → agent, v2). Identified by the command id.
	FrameCancel = "cancel"
)

Frame types.

View Source
const (
	CodeNotFound      = "not_found"
	CodeConflict      = "conflict"
	CodeNotModified   = "not_modified"
	CodeInvalid       = "invalid"
	CodeUnavailable   = "unavailable"
	CodeCanceled      = "canceled"
	CodeUnimplemented = "unimplemented"
	CodeInternal      = "internal"
)

Error codes: the daemon's typed answers (errdefs), flattened for the wire so the control plane can re-wrap them and keep IsNotFound/IsConflict working across the channel.

View Source
const ChunkSize = 32 << 10

ChunkSize bounds one stream chunk; small enough to interleave fairly with other traffic on a shared channel, large enough to keep log following cheap.

View Source
const (
	// MethodContainerExecAttach is the one BIDIRECTIONAL stream: after the
	// acknowledging result, output flows as chunks one way and input chunks
	// travel the other way under the same command id — an input chunk with
	// EOF closes the exec's stdin without ending the output.
	MethodContainerExecAttach = "ContainerExecAttach"
)

The command vocabulary: one name per dockerruntime.Runtime method carried over the channel. The executor refuses anything outside this list, and each name is what audit and telemetry record.

View Source
const MethodImageBuild = "ImageBuild"

MethodImageBuild (ADR-055 phase 2) runs a BuildKit build agent-side: the context is a host path local to the agent, the image lands in the local daemon's store, and only the progress stream crosses the channel. Streamed.

View Source
const StreamBuffer = 512

StreamBuffer bounds one stream's undelivered chunks. A channel is shared by every command to a server: rather than stall it behind one slow consumer, an overflowing stream is killed with an explicit error.

Variables

This section is empty.

Functions

func IsStreamMethod

func IsStreamMethod(method string) bool

IsStreamMethod reports whether the method answers with a chunk stream after its acknowledging result — what a relay must know to bridge it.

func PumpReader

func PumpReader(ctx context.Context, id int64, r io.Reader, write func(Frame) error)

PumpReader forwards a reader as stream chunks for command id until EOF or error, then sends the terminal chunk. brokeCleanly is decided by ctx: a canceled pump reports EOF-less silence, not a daemon error.

func Unavailable

func Unavailable(why string) error

Unavailable is the mandatory-agent failure mode (ADR-051): the channel is not there, the operation cannot run, and the remedy is the agent's reconciliation — never a silent fallback.

Types

type Attached

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

Attached is a bidirectional attach stream (ContainerExecAttach): reads carry the peer's output, writes its input.

func (*Attached) Close

func (s *Attached) Close() error

func (*Attached) CloseWrite

func (a *Attached) CloseWrite() error

CloseWrite closes the peer-side stdin; output keeps flowing.

func (*Attached) Read

func (s *Attached) Read(p []byte) (int, error)

func (*Attached) Write

func (a *Attached) Write(p []byte) (int, error)

type Command

type Command struct {
	ID     int64           `json:"id"`
	Method string          `json:"method"`
	Params json.RawMessage `json:"params,omitempty"`
}

Command is one typed method call. Method is a name from the enumerated vocabulary (method.go); Params is the JSON of that method's params struct — the Docker SDK types, which are the Engine API's own wire types.

type Conn

type Conn struct {

	// Record, when set, feeds the docker-ops counter: one increment per
	// command or stream open, by method and outcome.
	Record func(method, outcome string)
	// contains filtered or unexported fields
}

Conn routes typed commands over one live channel and matches results and stream chunks back by id. It is side-agnostic: the api process runs one per agent WebSocket, the relay client (ADR-052 §8) one per bridged server. The OWNER runs the read loop and feeds received frames in through DeliverResult/DeliverChunk; writes are serialized here.

func NewConn

func NewConn(ctx context.Context, conn *websocket.Conn) *Conn

NewConn wraps one live WebSocket whose lifetime is ctx.

func (*Conn) Attach

func (c *Conn) Attach(ctx context.Context, method string, params any) (*Attached, error)

Attach sends one bidirectional command: the result acknowledges the open, output arrives through Read, writes travel as input chunks under the same id, and CloseWrite marks the peer-side stdin closed without ending reads.

func (*Conn) CancelRemote

func (c *Conn) CancelRemote(id int64)

CancelRemote tells the peer to abort the command; best-effort — a broken socket is its own cancellation.

func (*Conn) Command

func (c *Conn) Command(ctx context.Context, method string, params any) (json.RawMessage, error)

Command sends one typed command and waits for its result.

func (*Conn) DeliverChunk

func (c *Conn) DeliverChunk(chunk *StreamChunk)

DeliverChunk routes a stream frame. A consumer that cannot keep up loses its stream — with an explicit error, and with the peer told to stop — so one slow log follower never stalls the whole channel.

func (*Conn) DeliverResult

func (c *Conn) DeliverResult(res *Result)

DeliverResult routes a result frame to its waiting call.

func (*Conn) Done

func (c *Conn) Done() <-chan struct{}

Done reports the connection's end — the owner's ctx.

func (*Conn) Stream

func (c *Conn) Stream(ctx context.Context, method string, params any) (io.ReadCloser, error)

Stream sends one streaming command: the result acknowledges the open, then chunks flow until EOF, error or Close.

func (*Conn) WriteFrame

func (c *Conn) WriteFrame(f Frame) error

WriteFrame serializes one frame onto the socket; 10 s bounds a stalled peer, not the command it carries.

type ContainerCreateParams

type ContainerCreateParams struct {
	Config           *container.Config         `json:"config"`
	HostConfig       *container.HostConfig     `json:"host_config,omitempty"`
	NetworkingConfig *network.NetworkingConfig `json:"networking_config,omitempty"`
	Platform         *ocispec.Platform         `json:"platform,omitempty"`
	Name             string                    `json:"name"`
}

type ContainerExecAttachParams

type ContainerExecAttachParams struct {
	ExecID  string                      `json:"exec_id"`
	Options container.ExecAttachOptions `json:"options"`
}

type ContainerExecCreateParams

type ContainerExecCreateParams struct {
	Name    string                `json:"name"`
	Options container.ExecOptions `json:"options"`
}

type ContainerExecResizeParams

type ContainerExecResizeParams struct {
	ExecID  string                  `json:"exec_id"`
	Options container.ResizeOptions `json:"options"`
}

type ContainerExecStartParams

type ContainerExecStartParams struct {
	ExecID  string                     `json:"exec_id"`
	Options container.ExecStartOptions `json:"options"`
}

type ContainerListParams

type ContainerListParams struct {
	Options container.ListOptions `json:"options"`
	Filters RawFilters            `json:"filters,omitempty"`
}

type ContainerLogsParams

type ContainerLogsParams struct {
	Name    string                `json:"name"`
	Options container.LogsOptions `json:"options"`
}

type ContainerRemoveParams

type ContainerRemoveParams struct {
	Name    string                  `json:"name"`
	Options container.RemoveOptions `json:"options"`
}

type ContainerRenameParams

type ContainerRenameParams struct {
	Name    string `json:"name"`
	NewName string `json:"new_name"`
}

type ContainerStartParams

type ContainerStartParams struct {
	Name    string                 `json:"name"`
	Options container.StartOptions `json:"options"`
}

type ContainerStopParams

type ContainerStopParams struct {
	Name    string                `json:"name"`
	Options container.StopOptions `json:"options"`
}

type ContainerWaitParams

type ContainerWaitParams struct {
	Name      string                  `json:"name"`
	Condition container.WaitCondition `json:"condition"`
}

type DirEnsureParams

type DirEnsureParams struct {
	Path string `json:"path"`
	Mode uint32 `json:"mode"`
}

type DiskUsageParams

type DiskUsageParams struct {
	Options types.DiskUsageOptions `json:"options"`
}

type Error

type Error struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

Error is a command failure on the wire.

func WireError

func WireError(err error) *Error

WireError flattens a daemon error into its wire form.

func (*Error) Err

func (e *Error) Err() error

Err rebuilds a typed error from its wire form: the message is preserved and the matching errdefs sentinel is wrapped in, so dockerruntime's predicates answer the same on both sides of the channel.

type EventsParams

type EventsParams struct {
	Options events.ListOptions `json:"options"`
	Filters RawFilters         `json:"filters,omitempty"`
}

type ExecToFileParams

type ExecToFileParams struct {
	Container string   `json:"container"`
	Cmd       []string `json:"cmd"`
	Path      string   `json:"path"`
	Mode      uint32   `json:"mode"`
	MakeDirs  bool     `json:"make_dirs,omitempty"`
	DirMode   uint32   `json:"dir_mode,omitempty"`
	Gzip      bool     `json:"gzip,omitempty"`
}

ExecToFileParams runs Cmd in Container and streams its stdout to Path — gzipped when Gzip is set. The digest and size describe the file as written (compressed), so a later FileHash comparison is byte-exact.

type ExecToFileResult

type ExecToFileResult struct {
	ExitCode  int    `json:"exit_code"`
	Stderr    string `json:"stderr,omitempty"` // tail — the diagnostic, not the payload
	SizeBytes int64  `json:"size_bytes"`
	SHA256    string `json:"sha256"`
}

type FileChownParams

type FileChownParams struct {
	Path string `json:"path"`
	UID  int    `json:"uid"`
	GID  int    `json:"gid"`
}

type FileCopyParams

type FileCopyParams struct {
	Src string `json:"src"`
	Dst string `json:"dst"`
}

type FileHashParams

type FileHashParams struct {
	Path string `json:"path"`
}

type FileHashResult

type FileHashResult struct {
	SHA256    string `json:"sha256"`
	SizeBytes int64  `json:"size_bytes"`
}

type FileReadParams

type FileReadParams struct {
	Path string `json:"path"`
	// MaxBytes bounds what travels back; 0 means the executor's default cap.
	MaxBytes int64 `json:"max_bytes,omitempty"`
}

type FileReadResult

type FileReadResult struct {
	Content []byte `json:"content,omitempty"`
	// Found is false for a missing file — absence is data here (an activity
	// file not yet written, an ACME store not yet initialized), not an error.
	Found     bool `json:"found"`
	Truncated bool `json:"truncated,omitempty"`
}

type FileRemoveParams

type FileRemoveParams struct {
	Path string `json:"path"`
	// Recursive removes a whole tree; either way an absent path is a no-op.
	Recursive bool `json:"recursive,omitempty"`
}

type FileStatParams

type FileStatParams struct {
	Path string `json:"path"`
}

type FileStatResult

type FileStatResult struct {
	Found bool  `json:"found"`
	IsDir bool  `json:"is_dir,omitempty"`
	Size  int64 `json:"size,omitempty"`
}

type FileToExecParams

type FileToExecParams struct {
	Path      string   `json:"path"`
	Gunzip    bool     `json:"gunzip,omitempty"`
	Container string   `json:"container"`
	Cmd       []string `json:"cmd"`
}

FileToExecParams streams Path — gunzipped when Gunzip is set — into the stdin of Cmd run in Container.

type FileToExecResult

type FileToExecResult struct {
	ExitCode int    `json:"exit_code"`
	Output   string `json:"output,omitempty"` // merged tail
}

type FileToURLParams

type FileToURLParams struct {
	Path    string            `json:"path"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers,omitempty"`
}

FileToURLParams uploads Path to URL with a plain PUT. The URL is presigned by the control plane and travels in this body over the encrypted channel — never argv, never a process list (INV-003).

type FileWriteParams

type FileWriteParams struct {
	Path    string `json:"path"`
	Content []byte `json:"content"`
	// Mode is applied explicitly after the write — the agent's umask never
	// decides what a key file ends up world-readable as.
	Mode uint32 `json:"mode"`
	// MakeDirs creates the missing parents with DirMode first.
	MakeDirs bool   `json:"make_dirs,omitempty"`
	DirMode  uint32 `json:"dir_mode,omitempty"`
	// Atomic stages the content next to Path and renames it into place, so a
	// concurrent reader (the proxy, the waker) never sees a partial file.
	Atomic bool `json:"atomic,omitempty"`
}

type Frame

type Frame struct {
	Type string `json:"type"`

	// Observation batching (v1 semantics, unchanged in v2).
	Seq          int64         `json:"seq,omitempty"`
	Observations []Observation `json:"observations,omitempty"`
	Denied       bool          `json:"denied,omitempty"`

	// Command traffic (v2).
	Cmd    *Command     `json:"cmd,omitempty"`
	Res    *Result      `json:"res,omitempty"`
	Chunk  *StreamChunk `json:"chunk,omitempty"`
	Cancel int64        `json:"cancel,omitempty"`
}

Frame is one message on the channel, both directions. Exactly one of the role-specific fields is set, per Type.

type ImageBuildParams

type ImageBuildParams struct {
	// ContextDir is the build context, a host path under the mounted tree.
	ContextDir string `json:"context_dir"`
	// Dockerfile is the dockerfile path RELATIVE to ContextDir.
	Dockerfile string            `json:"dockerfile"`
	Tags       []string          `json:"tags"`
	BuildArgs  map[string]string `json:"build_args,omitempty"`
	Secrets    map[string][]byte `json:"secrets,omitempty"`
	Labels     map[string]string `json:"labels,omitempty"`
	Target     string            `json:"target,omitempty"`
	NoCache    bool              `json:"no_cache,omitempty"`
}

ImageBuildParams describes one agent-side BuildKit build (ADR-055). The values that must never become image layers — the secrets — travel in this body over the encrypted channel and are mounted as BuildKit secrets, never exported as ARGs (INV-003, §5.2).

type ImageListParams

type ImageListParams struct {
	Options image.ListOptions `json:"options"`
	Filters RawFilters        `json:"filters,omitempty"`
}

type ImagePullParams

type ImagePullParams struct {
	Ref          string `json:"ref"`
	All          bool   `json:"all,omitempty"`
	RegistryAuth string `json:"registry_auth,omitempty"`
	Platform     string `json:"platform,omitempty"`
}

ImagePullParams carries the pull WITHOUT the SDK options struct: its PrivilegeFunc field is a func, which encoding/json refuses outright — the same "SDK type unfit for the wire" trap as filters.Args, caught the same day. The executor rebuilds image.PullOptions from these fields.

type ImagePushParams

type ImagePushParams struct {
	Ref          string            `json:"ref"`
	All          bool              `json:"all,omitempty"`
	RegistryAuth string            `json:"registry_auth,omitempty"`
	Platform     *ocispec.Platform `json:"platform,omitempty"`
}

ImagePushParams mirrors ImagePullParams for the same reason.

type ImageRemoveParams

type ImageRemoveParams struct {
	Image   string              `json:"image"`
	Options image.RemoveOptions `json:"options"`
}

type ImageTagParams

type ImageTagParams struct {
	Image string `json:"image"`
	Ref   string `json:"ref"`
}

type NameParams

type NameParams struct {
	Name string `json:"name"`
}

NameParams serves every method whose only parameter is the object's name or id: ContainerInspect, ContainerStatsOneShot, VolumeInspect, ImageInspect, NetworkRemove, …

type NetworkConnectParams

type NetworkConnectParams struct {
	Network   string                    `json:"network"`
	Container string                    `json:"container"`
	Config    *network.EndpointSettings `json:"config,omitempty"`
}

type NetworkCreateParams

type NetworkCreateParams struct {
	Name    string                `json:"name"`
	Options network.CreateOptions `json:"options"`
}

type NetworkDisconnectParams

type NetworkDisconnectParams struct {
	Network   string `json:"network"`
	Container string `json:"container"`
	Force     bool   `json:"force"`
}

type NetworkInspectParams

type NetworkInspectParams struct {
	Network string                 `json:"network"`
	Options network.InspectOptions `json:"options"`
}

type NetworkListParams

type NetworkListParams struct {
	Options network.ListOptions `json:"options"`
	Filters RawFilters          `json:"filters,omitempty"`
}

type Observation

type Observation struct {
	Type         string    `json:"type"`
	At           time.Time `json:"at"`
	Container    string    `json:"container,omitempty"`
	State        string    `json:"state,omitempty"`
	ResourceUUID string    `json:"resource_uuid,omitempty"`
}

Observation is one pushed fact (ADR-040). Types: "container_state" (a managed container changed state), "stz_woken" (a wake started containers), "heartbeat" (the agent is alive).

type PruneParams

type PruneParams struct {
	Filters RawFilters `json:"filters"`
}

PruneParams serves every *sPrune method.

type RawFilters

type RawFilters string

RawFilters is filters.Args in the SDK's canonical wire string (filters.ToJSON) — the ONLY form that survives a JSON round-trip; see the params comment above for why the SDK type itself does not.

func EncodeFilters

func EncodeFilters(f filters.Args) RawFilters

EncodeFilters renders args for the wire; an empty set encodes empty.

func (RawFilters) Decode

func (r RawFilters) Decode() (filters.Args, error)

Decode rebuilds the args; empty decodes to an empty set.

type RegistryLoginParams

type RegistryLoginParams struct {
	Auth registry.AuthConfig `json:"auth"`
}

type Result

type Result struct {
	ID   int64           `json:"id"`
	Body json.RawMessage `json:"body,omitempty"`
	Err  *Error          `json:"error,omitempty"`
}

Result answers the command with the same ID: a body (the method's return value as JSON) or an error, never both. A streaming method's Result only acknowledges the open; its output arrives as StreamChunks and its end as a chunk with EOF or Err set.

type StatsResult

type StatsResult struct {
	OSType string `json:"os_type"`
	Body   []byte `json:"body"`
}

StatsResult carries the one-shot stats snapshot: the daemon's OS type and the raw JSON body, re-wrapped into a StatsResponseReader on the caller side.

type StreamChunk

type StreamChunk struct {
	ID   int64  `json:"id"`
	Data []byte `json:"data,omitempty"`
	EOF  bool   `json:"eof,omitempty"`
	Err  *Error `json:"error,omitempty"`
}

StreamChunk is one piece of a command's output stream. Data is raw bytes (base64 on the wire); EOF marks a clean end, Err a broken one.

type URLToFileParams

type URLToFileParams struct {
	URL      string `json:"url"`
	Path     string `json:"path"`
	Mode     uint32 `json:"mode"`
	MakeDirs bool   `json:"make_dirs,omitempty"`
	DirMode  uint32 `json:"dir_mode,omitempty"`
}

URLToFileParams downloads URL into Path.

type VolumeCreateParams

type VolumeCreateParams struct {
	Options volume.CreateOptions `json:"options"`
}

type VolumeListParams

type VolumeListParams struct {
	Options volume.ListOptions `json:"options"`
	Filters RawFilters         `json:"filters,omitempty"`
}

type VolumeRemoveParams

type VolumeRemoveParams struct {
	Name  string `json:"name"`
	Force bool   `json:"force"`
}

Jump to

Keyboard shortcuts

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