From 1f64e4b6be1bf68272aa64ae99d37a949471af65 Mon Sep 17 00:00:00 2001 From: didericis Date: Tue, 21 Jul 2026 19:20:27 -0400 Subject: [PATCH] fix(macos): install passt for podman 5 rootless networking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit podman 5 uses pasta as the default rootless network helper; podman 4 used slirp4netns. The spike was written against bookworm's podman 4.3.1, so its package list never included passt — and the trixie bump (#451) changed the default out from under it. Every nested container failed to start with "could not find pasta", while image pulls and the service itself worked, which disguised a missing dependency as a compat-API bug. The bootstrap now checks for pasta up front so this fails with a clear message instead of surfacing at the first `docker run`. Note the sun_path fix in a12d2191 did NOT cause or cure this; that path was over the 108-byte limit independently and would have bitten as soon as attach was reached. Co-Authored-By: Claude Opus 4.8 --- .../macos_container/nested-containers-init.sh | 5 ++++- .../backend/macos_container/nested_containers.py | 13 ++++++++----- tests/unit/test_macos_nested_containers.py | 15 ++++++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/bot_bottle/backend/macos_container/nested-containers-init.sh b/bot_bottle/backend/macos_container/nested-containers-init.sh index 0c7c65f..8fcc363 100755 --- a/bot_bottle/backend/macos_container/nested-containers-init.sh +++ b/bot_bottle/backend/macos_container/nested-containers-init.sh @@ -7,7 +7,10 @@ if [ "$uid" -eq 0 ]; then exit 1 fi -for command in podman docker fuse-overlayfs slirp4netns; do +# 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 command -v "$command" >/dev/null 2>&1 || { echo "missing nested-container prerequisite: $command" >&2 exit 1 diff --git a/bot_bottle/backend/macos_container/nested_containers.py b/bot_bottle/backend/macos_container/nested_containers.py index 5f90965..5edb4fc 100644 --- a/bot_bottle/backend/macos_container/nested_containers.py +++ b/bot_bottle/backend/macos_container/nested_containers.py @@ -41,10 +41,9 @@ _INIT = "/usr/local/libexec/bot-bottle/nested-containers-init" # Deliberately cryptic and short. podman derives conmon's attach socket as # `$XDG_RUNTIME_DIR/libpod/tmp/socket/<64-hex-id>/attach`, and a Unix socket # path may not exceed 108 bytes (`sun_path`). The descriptive -# `/tmp/bot-bottle-podman-run` produced a 116-byte path, so every attached -# `docker run` failed with "unable to upgrade to tcp, received 500" while -# detached runs and image pulls worked fine. Do not lengthen this for -# readability — it buys 8 bytes of headroom over the limit. +# `/tmp/bot-bottle-podman-run` produced a 116-byte path — over the limit, so +# attach would have broken as soon as anything got far enough to attach. Do +# not lengthen this for readability; it buys 8 bytes of headroom. _RUNTIME_DIR = "/tmp/bbp" _SOCKET = f"{_RUNTIME_DIR}/podman.sock" _LOG = "/tmp/bot-bottle-nested-containers.log" @@ -83,8 +82,12 @@ 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. " && apt-get install -y --no-install-recommends " - "fuse-overlayfs slirp4netns uidmap \\\n" + "fuse-overlayfs 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 diff --git a/tests/unit/test_macos_nested_containers.py b/tests/unit/test_macos_nested_containers.py index 035a371..b064db5 100644 --- a/tests/unit/test_macos_nested_containers.py +++ b/tests/unit/test_macos_nested_containers.py @@ -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 slirp4netns uidmap", text) + self.assertIn("fuse-overlayfs passt slirp4netns uidmap", text) self.assertIn("USER node", text) self.assertTrue((Path(context) / "nested-containers-init.sh").is_file()) @@ -116,6 +116,19 @@ 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.""" + 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]) + def test_strips_subordinate_ranges_so_podman_avoids_newuidmap(self) -> None: """The single-UID fallback is the entire reason podman works here.