Tank.Image (Tank v0.1.0)

Copy Markdown View Source

Pulls an OCI / Docker image and assembles it into a root filesystem a container can run from.

pull/2 resolves an image reference, downloads the manifest and blobs from the registry (Tank.Image.Registry), verifies every blob against its digest, and extracts the layers into a root filesystem directory:

{:ok, %{rootfs: rootfs, config: config}} = Tank.Image.pull("alpine:latest")

The returned rootfs is the directory the container runtime pivots into; config is the parsed image config (entrypoint / env / user / ...), returned for the runtime to apply — pull/2 itself does not apply it.

Caching

Everything is cached on disk and survives restarts. The directory is the :cache option, else config :tank, image_cache: …, else ~/.cache/tank:

  • blobs/<algo>/<hex> — layer and config blobs, content-addressed;
  • rootfs/<digest> — the assembled rootfs, keyed by the image manifest digest, so an already-assembled image is reused;
  • refs/<ref>.json — a small sidecar mapping a reference to its resolved manifest digest and parsed config, written on every successful online pull. It is what lets a fully-cached image be served with no network I/O at all via offline: true.

An online pull always resolves the manifest (to follow a moving tag), but the heavy layer data downloads only on a cache miss — the log says pulling then, and using cached <ref> (<digest>) on a hit.

Layers extract as the user running the BEAM, so a pulled image satisfies a root-remapped (userns) rootfs-ownership rule with no chown.

Not yet handled: multi-layer whiteouts beyond a single directory's contents, and /dev-style runtime mounts (those are the container runtime's job).

Summary

Functions

Pulls ref and returns {:ok, %{rootfs: path, config: config}}.

Types

option()

@type option() :: {:cache, Path.t()} | {:offline, boolean()} | {:refresh, boolean()}

Options for pull/2:

  • :cache — cache directory (default: config :tank, image_cache: …, else ~/.cache/tank).
  • :offline — when true, never touch the network: resolve the manifest, config and rootfs entirely from cache, returning {:error, {:not_cached, ref}} if the image isn't there. Default false.
  • :refresh — when true, ignore cached blobs and the assembled rootfs and re-fetch from the registry (a warm cache is otherwise reused). Default false. Mutually exclusive with :offline.

pulled()

@type pulled() :: %{rootfs: Path.t(), config: map()}

Functions

pull(ref, opts \\ [])

@spec pull(String.t(), [option()]) :: {:ok, pulled()} | {:error, term()}

Pulls ref and returns {:ok, %{rootfs: path, config: config}}.

ref accepts name, name:tag, registry/name:tag and name@sha256:...; a bare name defaults to Docker Hub's library/ namespace and the latest tag. config is the parsed image config JSON (not yet applied to the workload -- returned for a later high-level run API).

See option/0 for :cache, :offline and :refresh.