The Kata Containers guest protocols in pure OCaml
README.md

kata #

The Kata Containers guest protocols in pure OCaml.

Kata Containers runs each container in its own lightweight VM. The host runtime creates the VM and then tells the agent inside it what to run, using ttrpc on a single vsock port. ttrpc is protobuf RPC over a raw stream socket, with no HTTP/2 in between. vsock (AF_VSOCK) is a socket family that carries traffic between a virtual machine and its host without a network; its addresses are a context id, which names the VM, and a port.

kata.agent is the guest side of that exchange written as OCaml values, and does no I/O itself. It has hand-written nox-protobuf codecs for the grpc.AgentService messages a host needs, plus the typed method table that names them. kata.oci encodes an OCI runtime configuration from the runc library as the agent's grpc.Spec message. That configuration is the config.json of the Open Container Initiative runtime specification, which gives the process to run, its root filesystem and mounts, its namespaces and its resource limits.

Installation #

$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam install kata

Usage #

Each RPC is a Ttrpc.Method.t that pairs the service-qualified name with the request and response codecs. A host, a test and the pure Ttrpc.Client state machine all drive a method in the same way:

# Ttrpc.Method.service Kata_agent.Agent.process_wait
- : string = "grpc.AgentService"
# Ttrpc.Method.name Kata_agent.Agent.read_stdout
- : string = "ReadStdout"
# Protobuf.to_string
    (Ttrpc.Method.request Kata_agent.Agent.process_wait)
    { Kata_agent.Process.Wait.container_id = "c1"; exec_id = "" }
- : string = "\n\002c1"

Over a connected vsock, Ttrpc_eio.Client.call issues the same method against a running agent:

let exit_status ~sw ~clock flow ~container_id =
  let client = Ttrpc_eio.Client.connect ~sw ~clock flow in
  match
    Ttrpc_eio.Client.call client Kata_agent.Agent.process_wait
      { Kata_agent.Process.Wait.container_id; exec_id = "" }
  with
  | Ok { status } -> Ok status
  | Error e -> Error (Fmt.str "%a" Ttrpc_eio.Client.pp_error e)

What is covered #

The service declares many more methods than kata.agent carries. The library has the ones a host needs to start a sandbox, run a container in it and watch that container: CreateSandbox, DestroySandbox, CreateContainer, StartContainer, ExecProcess, SignalProcess, WaitProcess, ReadStdout, ReadStderr, CopyFile and GetGuestDetails. agent.proto makes the first of those the precondition for the rest: "We allow only one sandbox per agent and implicitly require that CreateSandbox is called before other sandbox/network calls."

health.proto declares a second service on the same connection, grpc.Health, and Kata_agent.Health carries its Check, the liveness poll a host sends before anything else and which an agent with no sandbox still answers.

CreateContainerRequest.OCI and ExecProcessRequest.process are the two fields whose types come from oci.proto. kata.agent carries them as encoded message bodies without decoding them, so a request round-trips byte for byte (see Kata_agent.Oci). kata.oci builds those bodies from a Runc.Config.t or a Runc.Process.t, and refuses a configuration that grpc.Spec cannot express, such as a process umask, an idmapped mount or a seccomp default errno.

Spec #

protos/ vendors agent.proto and its import closure (oci.proto, csi.proto, types.proto and google/protobuf/empty.proto) verbatim from kata-containers/kata-containers, src/libs/protocols/protos/. They keep their upstream Apache-2.0 terms, and were taken at kata-containers commit 84a479111f1a08437ac2bfe232ab2590433da7ca. Each module cites the messages and field numbers it implements.

test/interop/kata-go/ replays wire bytes produced by google.golang.org/protobuf from those same vendored schemas, and asserts that this library's encoders produce identical bytes (REGEN=1 dune build @traces refreshes them).

  • kata-containers/kata-containers is the agent (Rust) and the host runtime (Go and Rust) this protocol connects.
  • ttrpc is the transport underneath, implementing containerd's PROTOCOL.md.
  • vaccel is another ttrpc agent protocol in OCaml, with the same shape and a different service.
  • nox-protobuf is the codec library the messages are written with.

License #

ISC. See LICENSE.md. The files under protos/ are third-party and keep their own terms; see protos/README.md.