# `Tank.Runtime`
[🔗](https://github.com/oshlabs/tank/blob/v0.2.0/lib/tank/runtime.ex#L1)

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 `execve`s 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`).

# `exec_context`

```elixir
@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.

# `begin_attach`

```elixir
@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`

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`

```elixir
@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`

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

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

# `host_pid`

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

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

# `start_link`

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

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
