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())