Run containers inside a bottle (nested_containers) #456
@@ -7,10 +7,12 @@ if [ "$uid" -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# pasta is podman 5's default rootless network helper. Checked here so a
|
||||
# missing package fails the bootstrap with a clear message rather than
|
||||
# letting every later `docker run` die with "could not find pasta".
|
||||
for command in podman docker fuse-overlayfs pasta slirp4netns; do
|
||||
# Every piece of podman 5's networking stack is checked here, because each
|
||||
# one fails at a different and misleading layer if it is absent: no pasta and
|
||||
# nothing starts at all; no nft and netavark cannot build the bridge every
|
||||
# compose file expects; no aardvark-dns and DNS inside nested containers fails
|
||||
# while everything else looks healthy.
|
||||
for command in podman docker fuse-overlayfs pasta nft slirp4netns; do
|
||||
command -v "$command" >/dev/null 2>&1 || {
|
||||
echo "missing nested-container prerequisite: $command" >&2
|
||||
exit 1
|
||||
|
||||
@@ -82,12 +82,21 @@ def build_image(
|
||||
"COPY --from=docker_cli /usr/local/libexec/docker/cli-plugins/"
|
||||
"docker-compose /usr/local/libexec/docker/cli-plugins/docker-compose\n"
|
||||
"RUN apt-get update \\\n"
|
||||
# passt provides `pasta`, which podman 5 uses by default for
|
||||
# rootless networking (podman 4 defaulted to slirp4netns). Without
|
||||
# it every container fails to start with "could not find pasta".
|
||||
# slirp4netns stays as the documented fallback.
|
||||
# podman 5's networking stack, installed explicitly because the
|
||||
# base image's `--no-install-recommends` podman does not pull it
|
||||
# in and each piece fails at a different, misleading layer:
|
||||
# passt -> `pasta`, the default rootless netns helper
|
||||
# (podman 4 used slirp4netns); without it
|
||||
# nothing starts: "could not find pasta"
|
||||
# nftables -> `nft`, which netavark shells out to for the
|
||||
# bridge network every compose file expects
|
||||
# aardvark-dns -> name resolution *inside* nested containers;
|
||||
# without it DNS fails while everything else
|
||||
# looks healthy
|
||||
# slirp4netns stays as the documented fallback for pasta.
|
||||
" && apt-get install -y --no-install-recommends "
|
||||
"fuse-overlayfs passt slirp4netns uidmap \\\n"
|
||||
"aardvark-dns fuse-overlayfs netavark nftables passt "
|
||||
"slirp4netns uidmap \\\n"
|
||||
" && rm -rf /var/lib/apt/lists/* \\\n"
|
||||
# Deliberate: an empty subordinate range keeps podman on the
|
||||
# single-UID mapping that needs no CAP_SYS_ADMIN. Adding ranges
|
||||
|
||||
@@ -108,7 +108,7 @@ class TestNestedContainersImage(unittest.TestCase):
|
||||
calls.append((image, context, dockerfile))
|
||||
text = Path(dockerfile).read_text(encoding="utf-8")
|
||||
self.assertIn("FROM agent:base", text)
|
||||
self.assertIn("fuse-overlayfs passt slirp4netns uidmap", text)
|
||||
self.assertIn("aardvark-dns fuse-overlayfs netavark nftables passt", text)
|
||||
self.assertIn("USER node", text)
|
||||
self.assertTrue((Path(context) / "nested-containers-init.sh").is_file())
|
||||
|
||||
@@ -116,18 +116,20 @@ class TestNestedContainersImage(unittest.TestCase):
|
||||
self.assertEqual("agent:base-nested-containers", image)
|
||||
self.assertEqual("agent:base-nested-containers", calls[0][0])
|
||||
|
||||
def test_installs_pasta_for_podman_5_rootless_networking(self) -> None:
|
||||
"""podman 5 defaults to pasta where podman 4 used slirp4netns. Without
|
||||
the passt package every nested container fails to start with "could
|
||||
not find pasta" — pulls still work, which makes it look like a
|
||||
compat-API bug rather than a missing dependency."""
|
||||
def test_installs_the_whole_podman_5_networking_stack(self) -> None:
|
||||
"""Each missing piece fails at a different, misleading layer: no pasta
|
||||
and nothing starts; no nft and netavark cannot build the bridge that
|
||||
compose expects; no aardvark-dns and DNS inside nested containers
|
||||
fails while everything else looks healthy. Image pulls keep working
|
||||
throughout, which is what made these read as compat-API bugs."""
|
||||
seen: list[str] = []
|
||||
|
||||
def build(_image: str, _context: str, *, dockerfile: str) -> None:
|
||||
seen.append(Path(dockerfile).read_text(encoding="utf-8"))
|
||||
|
||||
nested_containers.build_image("agent:base", build)
|
||||
self.assertIn("passt", seen[0])
|
||||
for package in ("passt", "nftables", "aardvark-dns"):
|
||||
self.assertIn(package, seen[0])
|
||||
|
||||
def test_strips_subordinate_ranges_so_podman_avoids_newuidmap(self) -> None:
|
||||
"""The single-UID fallback is the entire reason podman works here.
|
||||
|
||||
Reference in New Issue
Block a user