Skip to main content

lib/tank/runtime/etc.ex

defmodule Tank.Runtime.Etc do
  @moduledoc false

  # Per-pod /etc files materialised on the host and bound into the rootfs before
  # pivot (the image rootfs is shared and content-addressed, so it must not be
  # mutated per pod). Returns `{host_path, in_rootfs_path}` pairs for
  # `Tank.Runtime.Rootfs.setup/3`.

  alias Tank.{Host, Pod}

  @doc "Write resolv.conf + hosts into `dir`; return the bind pairs for the rootfs."
  @spec materialize(Pod.t(), Path.t()) :: [{Path.t(), Path.t()}]
  def materialize(%Pod{} = pod, dir) do
    File.mkdir_p!(dir)

    resolv = Path.join(dir, "resolv.conf")
    hosts = Path.join(dir, "hosts")
    File.write!(resolv, resolv_conf(pod))
    File.write!(hosts, hosts_file(pod))

    [{resolv, "/etc/resolv.conf"}, {hosts, "/etc/hosts"}]
  end

  # A networked pod that names its own DNS uses it; one that doesn't inherits the
  # host's (via the Tank.Host seam). `:none` / `:host` pods get an empty file.
  defp resolv_conf(%Pod{network: %Pod.Network{dns: [_ | _] = dns}}), do: nameservers(dns)
  defp resolv_conf(%Pod{network: %Pod.Network{dns: []}}), do: nameservers(Host.dns())
  defp resolv_conf(_pod), do: ""

  defp nameservers(list), do: Enum.map_join(list, fn ns -> "nameserver #{ns}\n" end)

  defp hosts_file(%Pod{name: name}) do
    "127.0.0.1\tlocalhost #{name}\n::1\tlocalhost ip6-localhost\n"
  end
end