From dd66886de88c12252597c7286e2c43d623bf8171 Mon Sep 17 00:00:00 2001 From: Viorel Munteanu Date: Fri, 4 Sep 2026 09:50:51 +0000 Subject: [PATCH] Add tests/setup-tests.py: fetch a real busybox fixture for [net] tests Idempotent: does nothing if images/busybox.tar already exists (your own unofficial build, or a previous fetch). Otherwise tries skopeo, then podman, then docker, in that order -- skopeo/podman both reliably produce a genuine OCI Image Layout tar; a plain `docker save` only does if the containerd image store happens to be enabled, so the result is verified (oci-layout/index.json actually present at the tar root) regardless of which tool produced it, falling through to the next option otherwise. Clear instructions + nonzero exit if none of the three are available and no fixture already exists. Verified the no-op (already-present) path and the no-tools-available error path directly (temporarily moved the existing images/busybox.tar aside and back) -- this dev machine has none of skopeo/podman/docker installed, so the actual fetch path itself is unverified here; the format-verification step (looks_like_oci_layout()) is what protects against a `docker save` that produced the legacy Docker tar format on some other machine. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz --- tests/setup-tests.py | 108 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100755 tests/setup-tests.py diff --git a/tests/setup-tests.py b/tests/setup-tests.py new file mode 100755 index 0000000..1f3cfa5 --- /dev/null +++ b/tests/setup-tests.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Fetches a real, runnable busybox OCI Image Layout tar for the +[integration][net] test categories (tests/support/fixtures.h's +find_busybox_fixture() looks for it at images/busybox.tar, relative to the +repo root -- the same directory this project's own manual testing has +always used, see CLAUDE.md). + +Idempotent: if images/busybox.tar already exists (your own unofficial +build, or a previously-fetched one), does nothing. Otherwise looks for +skopeo, then podman, then docker, in that preference order -- skopeo and +podman both reliably produce a genuine OCI Image Layout tar (what +oci_image.cpp actually parses: oci-layout + index.json + blobs/sha256/*); +a plain `docker save` only does if Docker's containerd image store happens +to be enabled, so the result is verified either way before being accepted. +""" + +import shutil +import subprocess +import sys +import tarfile +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +TARGET = REPO_ROOT / "images" / "busybox.tar" +IMAGE = "docker.io/library/busybox:latest" + + +def looks_like_oci_layout(path: Path) -> bool: + try: + with tarfile.open(path) as tar: + names = set(tar.getnames()) + except tarfile.TarError: + return False + return "oci-layout" in names and "index.json" in names + + +def fetch_with_skopeo() -> bool: + print("fetching busybox via skopeo...") + result = subprocess.run( + ["skopeo", "copy", f"docker://{IMAGE.removeprefix('docker.io/')}", f"oci-archive:{TARGET}:latest"] + ) + return result.returncode == 0 + + +def fetch_with_podman() -> bool: + print("fetching busybox via podman...") + if subprocess.run(["podman", "pull", IMAGE]).returncode != 0: + return False + result = subprocess.run(["podman", "save", "--format", "oci-archive", "-o", str(TARGET), IMAGE]) + return result.returncode == 0 + + +def fetch_with_docker() -> bool: + print("fetching busybox via docker (only works if the containerd image store is enabled)...") + if subprocess.run(["docker", "pull", IMAGE]).returncode != 0: + return False + result = subprocess.run(["docker", "save", "-o", str(TARGET), IMAGE]) + return result.returncode == 0 + + +def main() -> int: + if TARGET.exists(): + print(f"already present: {TARGET}") + return 0 + + TARGET.parent.mkdir(parents=True, exist_ok=True) + + fetchers = [ + ("skopeo", fetch_with_skopeo), + ("podman", fetch_with_podman), + ("docker", fetch_with_docker), + ] + for tool, fetch in fetchers: + if not shutil.which(tool): + continue + try: + ok = fetch() + except OSError as exc: + print(f"{tool} failed to run: {exc}", file=sys.stderr) + ok = False + if not ok: + TARGET.unlink(missing_ok=True) + print(f"{tool} was found but fetching busybox with it failed; trying the next option", file=sys.stderr) + continue + if not looks_like_oci_layout(TARGET): + TARGET.unlink(missing_ok=True) + print( + f"{tool} produced {TARGET.name}, but it isn't a genuine OCI Image Layout tar " + "(no oci-layout/index.json at its root) -- if that was docker, this usually means " + "the containerd image store isn't enabled; trying the next option", + file=sys.stderr, + ) + continue + print(f"fetched: {TARGET}") + return 0 + + print( + "no usable image tool found (checked skopeo, podman, docker) and " + f"{TARGET} doesn't already exist.\n" + "Install one of skopeo/podman/docker, or supply your own busybox-like " + f"OCI Image Layout tar at {TARGET} directly.", + file=sys.stderr, + ) + return 1 + + +if __name__ == "__main__": + sys.exit(main())