Tank.Runtime (Tank v0.1.0)

Copy Markdown View Source

Brings one pod to running reality and supervises it.

start_link/2 takes a %Tank.Pod{} and, at the Linx.Process :ready checkpoint, runs the full host-side bring-up:

  1. pull the image and derive run params (Tank.OCI),
  2. spawn the workload into namespaces, parked at the checkpoint,
  3. build the rootfs (Tank.Runtime.Rootfs) with per-pod /etc files,
  4. configure the network (Tank.Runtime.Network),
  5. apply cgroup limits,
  6. proceed — the workload execves inside its container.

Scope (M4)

One container per pod (sidecars are M7); the workload runs as the image's default user with no user namespace; container stdio goes to /dev/null (log capture is a later concern). A pod's netns may still hold several NICs.

Owner events

The :owner option receives:

  • {:tank, :running, host_pid} — configured and running.
  • {:tank, :exited, code} / {:tank, :signaled, signum} / {:tank, :error, reason} — terminal.

The GenServer stops when its workload terminates: a clean exit → :normal, a non-zero exit or signal → {:shutdown, {:workload_exited | :workload_signaled, …}} (an expected outcome, so OTP logs no crash report), and a genuine setup or session failure → an abnormal reason. The reconciler reads the reason and rebuilds the whole composite — a fresh rootfs and namespace — per the pod's :restart policy.

Options

  • :owner — pid for {:tank, _} events (default: none).
  • :data_dir — base dir for per-pod scratch (<data_dir>/run/<pod>); defaults to :tank, :data_dir or a tmp dir.
  • :image — keyword opts forwarded to Tank.Image.pull/2 (e.g. :cache).

Summary

Types

What Tank.exec/3 needs to enter the container: the workload's host pid plus the container's resolved env (image Env merged with the spec's) and working_dir — so an exec session inherits the container's environment, not the host's.

Functions

Begin an attach to a tty: true container's main process: hand the session's event stream to attacher and return the session pid for Linx.Tty.attach/3.

Supervisor child spec. restart: :temporary — OTP never restarts a runtime; Tank.Reconciler owns restart (it monitors the runtime and acts on the stop reason per the pod's :restart policy). Pod policy lives in the reconciler, not the child spec.

End an attach: take ownership of the session back and re-derive the workload's state. If it terminated while detached, the runtime acts on it now (stopping so the reconciler applies the restart policy). Best-effort — a no-op if the runtime is already gone.

The container's exec context, once :running. {:error, :not_running} before then.

The workload's host pid, once :running. {:error, :not_running} before then.

Start and bring up one pod. See the moduledoc for options.

Types

exec_context()

@type exec_context() :: %{
  host_pid: pos_integer(),
  env: [String.t()],
  working_dir: String.t()
}

What Tank.exec/3 needs to enter the container: the workload's host pid plus the container's resolved env (image Env merged with the spec's) and working_dir — so an exec session inherits the container's environment, not the host's.

Functions

begin_attach(runtime, attacher)

@spec begin_attach(pid(), pid()) :: {:ok, pid()} | {:error, :not_running | :not_a_tty}

Begin an attach to a tty: true container's main process: hand the session's event stream to attacher and return the session pid for Linx.Tty.attach/3.

{:error, :not_running} if the workload isn't up yet; {:error, :not_a_tty} if the container wasn't started with tty: true (there is no PTY to attach to). Pair every success with end_attach/1.

child_spec(init_arg)

Supervisor child spec. restart: :temporary — OTP never restarts a runtime; Tank.Reconciler owns restart (it monitors the runtime and acts on the stop reason per the pod's :restart policy). Pod policy lives in the reconciler, not the child spec.

end_attach(runtime)

@spec end_attach(pid()) :: :ok

End an attach: take ownership of the session back and re-derive the workload's state. If it terminated while detached, the runtime acts on it now (stopping so the reconciler applies the restart policy). Best-effort — a no-op if the runtime is already gone.

exec_context(runtime)

@spec exec_context(pid()) :: {:ok, exec_context()} | {:error, :not_running}

The container's exec context, once :running. {:error, :not_running} before then.

host_pid(runtime)

@spec host_pid(pid()) :: {:ok, pos_integer()} | {:error, :not_running}

The workload's host pid, once :running. {:error, :not_running} before then.

start_link(pod, opts \\ [])

@spec start_link(
  Tank.Pod.t(),
  keyword()
) :: GenServer.on_start()

Start and bring up one pod. See the moduledoc for options.