mirror of
https://github.com/unslothai/unsloth.git
synced 2026-10-21 14:00:32 +00:00
Comment and docstring changes only, in test files, split out of #12968 so the test-only part can merge first.
887 lines
37 KiB
Python
887 lines
37 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-Present the Unsloth team. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""docker/install_nvidia_toolkit.sh against stubbed package managers, drivers and
|
|
Docker daemons, plus docker/run.sh's offer to run it."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
INSTALLER = REPO_ROOT / "docker" / "install_nvidia_toolkit.sh"
|
|
RUN_SH = REPO_ROOT / "docker" / "run.sh"
|
|
|
|
OS_RELEASE = {
|
|
"ubuntu": 'ID=ubuntu\nID_LIKE=debian\nVERSION_ID="24.04"\n',
|
|
"debian": "ID=debian\nVERSION_ID=12\n",
|
|
"rhel": 'ID="rhel"\nID_LIKE="fedora"\nVERSION_ID="9.4"\n',
|
|
"fedora": "ID=fedora\nVERSION_ID=41\n",
|
|
"amzn": 'ID="amzn"\nID_LIKE="fedora"\nVERSION_ID="2023"\n',
|
|
"rocky": 'ID="rocky"\nID_LIKE="rhel centos fedora"\n',
|
|
"suse": 'ID="opensuse-leap"\nID_LIKE="suse opensuse"\n',
|
|
"alpine": "ID=alpine\n",
|
|
}
|
|
|
|
|
|
# Only these real tools, so an absent command (dnf, systemctl) never hits the runner's own.
|
|
_REAL_TOOLS = (
|
|
"bash",
|
|
"grep",
|
|
"sed",
|
|
"head",
|
|
"tr",
|
|
"sort",
|
|
"mkdir",
|
|
"cat",
|
|
"basename",
|
|
"dirname",
|
|
"touch",
|
|
"mv",
|
|
"cp",
|
|
"chmod",
|
|
"stat",
|
|
"cut",
|
|
"rm",
|
|
"true",
|
|
# Pausing mid-output makes the SIGPIPE cases deterministic.
|
|
"sleep",
|
|
)
|
|
|
|
|
|
def _isolated_path(tmp_path: Path, bindir: Path) -> str:
|
|
sysbin = tmp_path / "sysbin"
|
|
sysbin.mkdir(exist_ok = True)
|
|
for name in _REAL_TOOLS:
|
|
real = shutil.which(name)
|
|
assert real, f"{name} not found on this host"
|
|
(sysbin / name).symlink_to(real)
|
|
return f"{bindir}{os.pathsep}{sysbin}"
|
|
|
|
|
|
def _stub(path: Path, body: str) -> None:
|
|
path.write_text("#!/usr/bin/env bash\n" + body, encoding = "utf-8")
|
|
path.chmod(path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
|
|
|
|
def _setup(
|
|
tmp_path: Path,
|
|
*,
|
|
distro: str = "ubuntu",
|
|
driver: bool = True,
|
|
configured: bool = False,
|
|
toolkit_installed: bool | None = None,
|
|
desktop: bool = False,
|
|
systemctl: str = "ok",
|
|
verify_ok: bool = True,
|
|
uid: int = 0,
|
|
wsl: bool = False,
|
|
driver_version: str = "580.65.06",
|
|
rootless: bool | str = False,
|
|
chunked: bool = False,
|
|
gpus: int = 1,
|
|
desktop_os: str = "Docker Desktop",
|
|
desktop_kernel: str = "5.15.167.4-microsoft-standard-WSL2",
|
|
desktop_info_fails: bool = False,
|
|
) -> tuple[Path, Path, dict]:
|
|
bindir = tmp_path / "bin"
|
|
bindir.mkdir()
|
|
root = tmp_path / "root"
|
|
root.mkdir()
|
|
log = tmp_path / "calls.log"
|
|
marker = tmp_path / "configured"
|
|
if configured:
|
|
marker.write_text("", encoding = "utf-8")
|
|
rec = f'echo "$(basename "$0") $*" >> {log}\n'
|
|
_stub(bindir / "id", f"echo {uid}\n")
|
|
_stub(bindir / "sudo", rec + "exit 0\n")
|
|
# The pause lets an early-exiting consumer (head -1) close the pipe mid-write.
|
|
pause = "sleep 0.05\n" if chunked else ""
|
|
query = "".join(f'echo " {driver_version}"\n{pause}' for _ in range(gpus)) + "exit 0\n"
|
|
_stub(
|
|
bindir / "nvidia-smi",
|
|
(
|
|
f'if [ "$1" = --query-gpu=driver_version ]; then\n{query}fi\n'
|
|
'echo "GPU 0: NVIDIA H100 (UUID: GPU-abc)"\n'
|
|
)
|
|
if driver
|
|
else "exit 9\n",
|
|
)
|
|
# The installer picks the verification platform from uname -m.
|
|
_stub(bindir / "uname", 'if [ "$1" = -s ]; then echo Linux; else echo x86_64; fi\n')
|
|
ctk = bindir / "nvidia-ctk"
|
|
runtime = bindir / "nvidia-container-runtime"
|
|
ctk_body = rec + f"touch {marker}\n"
|
|
if configured or toolkit_installed:
|
|
_stub(ctk, ctk_body)
|
|
_stub(runtime, "exit 0\n")
|
|
(tmp_path / "ctk-stub-body").write_text("#!/usr/bin/env bash\n" + ctk_body, encoding = "utf-8")
|
|
_stub(
|
|
bindir / "docker",
|
|
rec
|
|
+ 'if [ "$1" = context ]; then case "${DOCKER_CONTEXT:-}" in remote*) echo "tcp://gpu-box:2376" ;; missing*) echo "context not found" >&2; exit 1 ;; *) echo "unix:///var/run/docker.sock" ;; esac; exit 0; fi\n'
|
|
+ 'if [ "$1" = info ]; then\n'
|
|
+ (
|
|
' case "$3" in *OperatingSystem*) echo "cannot connect to the Docker daemon" >&2; exit 1 ;; esac\n'
|
|
if desktop_info_fails
|
|
else f' case "$3" in *OperatingSystem*) echo "{desktop_os}|{desktop_kernel}"; exit 0 ;; esac\n'
|
|
)
|
|
+ (' echo " Operating System: Docker Desktop"\n' if desktop else "")
|
|
# Split output so a `grep -q` consumer matches before the rest is written.
|
|
+ (" sleep 0.05\n" if chunked else "")
|
|
+ (
|
|
' if [ "$2" = "--format" ]; then\n'
|
|
' case "${DOCKER_HOST:-}" in *docker.sock) echo "name=rootless,name=seccomp" ;; *) echo "name=seccomp" ;; esac; exit 0\n'
|
|
" fi\n"
|
|
if rootless == "via_sudo_uid"
|
|
else (
|
|
' if [ "$2" = "--format" ]; then echo "name=rootless,name=seccomp"; exit 0; fi\n'
|
|
if rootless
|
|
else ""
|
|
)
|
|
)
|
|
+ f' if [ -e {marker} ]; then echo " Runtimes: io.containerd.runc.v2 nvidia runc"; else echo " Runtimes: io.containerd.runc.v2 runc"; fi\n'
|
|
+ (' sleep 0.05\n echo " Default Runtime: runc"\n' if chunked else "")
|
|
+ " exit 0\nfi\n"
|
|
+ (
|
|
"exit 0\n"
|
|
if verify_ok
|
|
else 'echo "docker: could not select device driver" >&2; exit 125\n'
|
|
),
|
|
)
|
|
installs = (
|
|
f'case "$*" in *install*nvidia-container-toolkit*) cp {tmp_path / "ctk-stub-body"} {ctk}; chmod 755 {ctk};'
|
|
f' printf "#!/usr/bin/env bash\\nexit 0\\n" > {runtime}; chmod 755 {runtime} ;; esac\n'
|
|
)
|
|
for pm in ("apt-get", "dnf", "yum", "zypper"):
|
|
_stub(bindir / pm, rec + installs + "exit 0\n")
|
|
_stub(bindir / "service", rec + "exit 0\n")
|
|
if systemctl == "ok":
|
|
_stub(bindir / "systemctl", rec + "exit 0\n")
|
|
elif systemctl == "fails":
|
|
_stub(bindir / "systemctl", rec + "exit 1\n")
|
|
_stub(
|
|
bindir / "curl",
|
|
rec + 'case "$*" in\n'
|
|
' *gpgkey) printf "FAKE-ARMORED-KEY\\n" ;;\n'
|
|
' *.list) if [ -e "$LIST_DOWNLOAD_FAILS" ]; then echo "curl: (22) 503" >&2; exit 22; fi; printf "deb https://nvidia.github.io/libnvidia-container/stable/deb/\\$(ARCH) /\\n" ;;\n'
|
|
' *.repo) printf "[nvidia-container-toolkit]\\nbaseurl=https://nvidia.github.io/libnvidia-container/stable/rpm/\\$basearch\\n" ;;\n'
|
|
"esac\n",
|
|
)
|
|
_stub(
|
|
bindir / "gpg",
|
|
rec + 'while [ $# -gt 0 ]; do if [ "$1" = -o ]; then out="$2"; shift; fi; shift; done\n'
|
|
'printf "DEARMORED:"; cat > "$out"\n',
|
|
)
|
|
osr = tmp_path / "os-release"
|
|
osr.write_text(OS_RELEASE[distro], encoding = "utf-8")
|
|
procv = tmp_path / "proc-version"
|
|
procv.write_text(
|
|
"Linux version 5.15.167.4-microsoft-standard-WSL2\n"
|
|
if wsl
|
|
else "Linux version 6.8.0-45-generic\n",
|
|
encoding = "utf-8",
|
|
)
|
|
env = dict(os.environ)
|
|
env["PATH"] = _isolated_path(tmp_path, bindir)
|
|
env["UNSLOTH_DESTDIR"] = str(root)
|
|
env["LIST_DOWNLOAD_FAILS"] = str(tmp_path / "list-download-fails")
|
|
env["UNSLOTH_OS_RELEASE"] = str(osr)
|
|
env["UNSLOTH_PROC_VERSION"] = str(procv)
|
|
env["UNSLOTH_RUN_USER_DIR"] = str(tmp_path / "run-user")
|
|
env["UNSLOTH_WSL_LIB_DIR"] = str(tmp_path / "wsl-lib")
|
|
env.pop("UNSLOTH_TOOLKIT_VERIFY", None)
|
|
env.pop("SUDO_UID", None)
|
|
env.pop("DOCKER_HOST", None)
|
|
env.pop("DOCKER_CONTEXT", None)
|
|
return root, log, env
|
|
|
|
|
|
def _run(
|
|
env: dict,
|
|
script: Path = INSTALLER,
|
|
extra_env: dict | None = None,
|
|
umask: str | None = None,
|
|
) -> subprocess.CompletedProcess:
|
|
e = dict(env)
|
|
e.update(extra_env or {})
|
|
cmd = ["bash", str(script)]
|
|
if umask:
|
|
cmd = ["bash", "-c", f'umask {umask}; exec bash "$0"', str(script)]
|
|
return subprocess.run(cmd, capture_output = True, text = True, env = e, timeout = 60)
|
|
|
|
|
|
def _calls(log: Path) -> list[str]:
|
|
return log.read_text(encoding = "utf-8").splitlines() if log.exists() else []
|
|
|
|
|
|
def test_ubuntu_gets_the_apt_recipe_then_docker_is_configured_restarted_and_verified(
|
|
tmp_path: Path,
|
|
):
|
|
root, log, env = _setup(tmp_path, distro = "ubuntu")
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert "apt-get update -qq" in calls
|
|
assert any(
|
|
c.startswith("apt-get install") and c.endswith("nvidia-container-toolkit") for c in calls
|
|
)
|
|
key = root / "usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"
|
|
assert key.read_text(encoding = "utf-8") == "FAKE-ARMORED-KEY\n"
|
|
lst = (root / "etc/apt/sources.list.d/nvidia-container-toolkit.list").read_text(
|
|
encoding = "utf-8"
|
|
)
|
|
assert lst.startswith(
|
|
"deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://"
|
|
)
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in calls
|
|
assert "systemctl restart docker" in calls
|
|
assert (
|
|
calls[-1] == "docker run --rm --gpus all --platform linux/amd64 ubuntu:24.04 nvidia-smi -L"
|
|
)
|
|
assert not any(c.startswith(("dnf", "yum", "zypper")) for c in calls)
|
|
|
|
|
|
@pytest.mark.parametrize("distro", ["rhel", "fedora", "amzn", "rocky"])
|
|
def test_rpm_distributions_use_dnf_and_the_repo_file(tmp_path: Path, distro: str):
|
|
root, log, env = _setup(tmp_path, distro = distro)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert "dnf install -y nvidia-container-toolkit" in calls
|
|
repo = (root / "etc/yum.repos.d/nvidia-container-toolkit.repo").read_text(encoding = "utf-8")
|
|
assert repo.startswith("[nvidia-container-toolkit]")
|
|
assert not any(c.startswith(("apt-get", "yum", "zypper")) for c in calls)
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in calls
|
|
|
|
|
|
def test_yum_when_dnf_is_absent(tmp_path: Path):
|
|
root, log, env = _setup(tmp_path, distro = "rhel")
|
|
(tmp_path / "bin" / "dnf").unlink()
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "yum install -y nvidia-container-toolkit" in _calls(log)
|
|
|
|
|
|
def test_suse_uses_zypper(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, distro = "suse")
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert any(c.startswith("zypper --non-interactive ar https://nvidia.github.io/") for c in calls)
|
|
assert (
|
|
"zypper --non-interactive --gpg-auto-import-keys install nvidia-container-toolkit" in calls
|
|
)
|
|
assert not any(c.startswith(("apt-get", "dnf", "yum")) for c in calls)
|
|
|
|
|
|
def test_an_unknown_distribution_stops_before_touching_anything(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, distro = "alpine")
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "unrecognised distribution" in res.stderr
|
|
assert "install-guide" in res.stderr
|
|
assert not any(
|
|
c.startswith(("apt-get", "dnf", "yum", "zypper", "nvidia-ctk")) for c in _calls(log)
|
|
)
|
|
|
|
|
|
def test_no_driver_means_stop_and_say_how_to_get_one(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, driver = False)
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "no NVIDIA driver found" in res.stderr
|
|
assert "570.26" in res.stderr
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk", "docker run")) for c in _calls(log))
|
|
|
|
|
|
def test_an_old_driver_is_reported_after_the_toolkit_is_in_place(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, driver_version = "550.54.15")
|
|
res = _run(env)
|
|
assert res.returncode == 3
|
|
assert "550.54.15 is below 570.26" in res.stderr
|
|
calls = _calls(log)
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in calls
|
|
assert any(c.startswith("docker run") for c in calls)
|
|
|
|
|
|
def test_a_current_driver_is_confirmed(tmp_path: Path):
|
|
_, _, env = _setup(tmp_path, driver_version = "570.26")
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "570.26 meets the 570.26 minimum" in res.stdout
|
|
|
|
|
|
def test_rootless_docker_is_refused_before_anything_runs(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, rootless = True, uid = 1000)
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "rootless" in res.stderr and "#rootless-mode" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get", "nvidia-ctk")) for c in _calls(log))
|
|
|
|
|
|
def test_rootless_docker_is_still_caught_when_piped_into_sudo_bash(tmp_path: Path):
|
|
"""env_reset strips DOCKER_HOST but keeps SUDO_UID, so the user's socket is probed through it."""
|
|
import socket
|
|
|
|
_, log, env = _setup(tmp_path, rootless = "via_sudo_uid", uid = 0)
|
|
sock_dir = tmp_path / "run-user" / "1000"
|
|
sock_dir.mkdir(parents = True)
|
|
s = socket.socket(socket.AF_UNIX)
|
|
cwd = os.getcwd()
|
|
os.chdir(sock_dir) # AF_UNIX paths are capped at 108 bytes; bind relative
|
|
try:
|
|
s.bind("docker.sock")
|
|
finally:
|
|
os.chdir(cwd)
|
|
env["SUDO_UID"] = "1000"
|
|
res = _run(env)
|
|
s.close()
|
|
assert res.returncode == 2
|
|
assert "rootless" in res.stderr
|
|
assert not res.stderr.rstrip().endswith(" 2"), "the exit code leaked into the message"
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk")) for c in _calls(log))
|
|
|
|
|
|
def test_without_docker_the_script_says_so(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path)
|
|
(tmp_path / "bin" / "docker").unlink()
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "docker is not installed" in res.stderr
|
|
assert _calls(log) == []
|
|
|
|
|
|
def test_docker_context_wins_over_a_local_docker_host(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, uid = 1000)
|
|
env["DOCKER_HOST"] = "unix:///var/run/docker.sock"
|
|
env["DOCKER_CONTEXT"] = "remote-gpu"
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get")) for c in _calls(log))
|
|
|
|
|
|
def test_nvidia_smi_is_found_in_the_wsl_library_dir_after_sudo(tmp_path: Path):
|
|
"""secure_path drops /usr/lib/wsl/lib, where the Windows driver keeps nvidia-smi."""
|
|
_, log, env = _setup(tmp_path, wsl = True)
|
|
smi = tmp_path / "bin" / "nvidia-smi"
|
|
wsl_lib = tmp_path / "wsl-lib"
|
|
wsl_lib.mkdir()
|
|
smi.rename(wsl_lib / "nvidia-smi")
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in _calls(log)
|
|
assert "meets the 570.26 minimum" in res.stdout
|
|
|
|
|
|
def test_a_failed_source_list_download_keeps_the_existing_file(tmp_path: Path):
|
|
root, log, env = _setup(tmp_path, distro = "ubuntu")
|
|
lst = root / "etc/apt/sources.list.d/nvidia-container-toolkit.list"
|
|
lst.parent.mkdir(parents = True)
|
|
lst.write_text("deb [signed-by=/usr/share/keyrings/x.gpg] https://old /\n", encoding = "utf-8")
|
|
(tmp_path / "list-download-fails").write_text("", encoding = "utf-8")
|
|
res = _run(env)
|
|
assert res.returncode != 0
|
|
assert lst.read_text(encoding = "utf-8").startswith(
|
|
"deb [signed-by=/usr/share/keyrings/x.gpg] https://old"
|
|
)
|
|
assert not any(c.startswith("nvidia-ctk") for c in _calls(log))
|
|
|
|
|
|
def test_an_unsupported_architecture_is_refused_before_any_install(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Linux; else echo ppc64le; fi\n')
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "unsupported architecture ppc64le" in res.stderr
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk", "docker run")) for c in _calls(log))
|
|
|
|
|
|
def test_an_installed_toolkit_is_only_registered(tmp_path: Path):
|
|
"""nvidia-ctk present, runtime entry gone: no repository or package work, as an offline host needs."""
|
|
_, log, env = _setup(tmp_path, toolkit_installed = True)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert not any(c.startswith(("apt-get", "dnf", "yum", "zypper", "curl")) for c in calls)
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in calls
|
|
assert "systemctl restart docker" in calls
|
|
|
|
|
|
def test_the_keyring_is_world_readable_under_a_strict_umask(tmp_path: Path):
|
|
root, _, env = _setup(tmp_path, distro = "ubuntu")
|
|
res = _run(env, umask = "077")
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
key = root / "usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"
|
|
assert oct(key.stat().st_mode & 0o777) == "0o644"
|
|
|
|
|
|
def test_an_uninspectable_context_is_an_error_not_the_local_daemon(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path)
|
|
env["DOCKER_CONTEXT"] = "missing-in-root-config"
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "cannot inspect the Docker context 'missing-in-root-config'" in res.stderr
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
def test_no_gpu_on_wsl_points_at_the_windows_driver(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, driver = False, wsl = True)
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "NVIDIA Windows driver" in res.stderr and "never a Linux driver" in res.stderr
|
|
assert "ubuntu-drivers" not in res.stderr
|
|
assert not any(c.startswith("apt-get") for c in _calls(log))
|
|
|
|
|
|
def test_only_the_toolkit_cli_present_still_installs_the_full_package(tmp_path: Path):
|
|
"""nvidia-container-toolkit-base ships nvidia-ctk without the runtime binary."""
|
|
_, log, env = _setup(tmp_path, toolkit_installed = True)
|
|
(tmp_path / "bin" / "nvidia-container-runtime").unlink()
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert any(
|
|
c.startswith("apt-get install") and c.endswith("nvidia-container-toolkit") for c in calls
|
|
)
|
|
assert "nvidia-ctk runtime configure --runtime=docker" in calls
|
|
|
|
|
|
def test_a_remote_docker_endpoint_is_refused(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, uid = 1000)
|
|
env["DOCKER_HOST"] = "tcp://gpu-box:2376"
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get", "nvidia-ctk")) for c in _calls(log))
|
|
|
|
|
|
def test_docker_desktop_on_native_linux_is_refused(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False, uid = 1000)
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "Docker Desktop for Linux has no NVIDIA GPU support" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get")) for c in _calls(log))
|
|
|
|
|
|
def test_a_runtime_registered_without_nvidia_ctk_is_left_alone(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, configured = True)
|
|
(tmp_path / "bin" / "nvidia-ctk").unlink()
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
calls = _calls(log)
|
|
assert not any(c.startswith(("apt-get", "dnf", "curl", "systemctl")) for c in calls)
|
|
assert "docker run --rm --gpus all --platform linux/amd64 ubuntu:24.04 nvidia-smi -L" in calls
|
|
|
|
|
|
def test_a_second_daemon_on_its_own_socket_is_refused(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, uid = 1000)
|
|
env["DOCKER_HOST"] = "unix:///run/dockerd-b/docker.sock"
|
|
res = _run(env)
|
|
assert res.returncode == 2
|
|
assert "not the system daemon" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get")) for c in _calls(log))
|
|
|
|
|
|
def test_the_default_sockets_are_accepted(tmp_path: Path):
|
|
for i, sock in enumerate(("unix:///var/run/docker.sock", "unix:///run/docker.sock")):
|
|
sub = tmp_path / f"case{i}"
|
|
sub.mkdir()
|
|
_, log, env = _setup(sub, uid = 1000)
|
|
env["DOCKER_HOST"] = sock
|
|
res = _run(env)
|
|
assert "not the system daemon" not in res.stderr, sock
|
|
|
|
|
|
def test_docker_desktop_is_still_seen_when_the_daemon_answers_in_pieces(tmp_path: Path):
|
|
"""grep -q exiting early sends the producer SIGPIPE (141), so under pipefail a match reads as a miss."""
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False, uid = 1000, chunked = True)
|
|
res = _run(env)
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "Docker Desktop for Linux has no NVIDIA GPU support" in res.stderr
|
|
assert not any(c.startswith(("sudo", "apt-get")) for c in _calls(log))
|
|
|
|
|
|
def test_a_chunked_daemon_does_not_hide_the_nvidia_runtime(tmp_path: Path):
|
|
"""Same pipeline shape in `configured()`, and the cost is a pointless reinstall."""
|
|
_, log, env = _setup(tmp_path, configured = True, chunked = True)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "already lists the nvidia runtime" in res.stdout
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk")) for c in _calls(log))
|
|
|
|
|
|
def test_the_driver_version_survives_a_multi_gpu_host(tmp_path: Path):
|
|
"""A SIGPIPE from head -1 in a command substitution under set -e ends the script silently."""
|
|
_, _, env = _setup(tmp_path, chunked = True, gpus = 8)
|
|
res = _run(env)
|
|
assert res.returncode == 0, f"rc={res.returncode} out={res.stdout!r} err={res.stderr!r}"
|
|
assert res.stdout.strip(), "the script exited without printing anything"
|
|
|
|
|
|
def test_docker_desktop_on_macos_says_cpu_only(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False, uid = 1000)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "no NVIDIA GPU" in res.stdout and "UNSLOTH_ALLOW_CPU=1" in res.stdout
|
|
assert "GPU support comes with it" not in res.stdout
|
|
|
|
|
|
def test_macos_says_cpu_only_whatever_runs_the_daemon(tmp_path: Path):
|
|
"""colima and Rancher Desktop keep their socket outside /var/run, which took a Mac
|
|
to the endpoint check: it told the user to configure that daemon by hand, for a
|
|
toolkit no Mac can use."""
|
|
_, _, env = _setup(tmp_path, driver = False, uid = 1000)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env, extra_env = {"DOCKER_HOST": "unix:///Users/u/.colima/default/docker.sock"})
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "no NVIDIA GPU" in res.stdout and "UNSLOTH_ALLOW_CPU=1" in res.stdout
|
|
assert "by hand" not in res.stderr, res.stderr
|
|
|
|
|
|
def test_a_mac_driving_a_remote_daemon_is_sent_to_that_host(tmp_path: Path):
|
|
"""The one Mac the toolkit concerns: a CLI pointed at a Linux box over tcp:// or
|
|
ssh://. "Nothing to install" there is wrong; the box needs it."""
|
|
_, _, env = _setup(tmp_path, driver = False, uid = 1000)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env, extra_env = {"DOCKER_HOST": "tcp://gpu-box:2376"})
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
assert "nothing to install" not in res.stdout
|
|
res = _run(
|
|
env,
|
|
extra_env = {"DOCKER_HOST": "unix:///var/run/docker.sock", "DOCKER_CONTEXT": "remote-gpu"},
|
|
)
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
res = _run(env, extra_env = {"DOCKER_HOST": "gpu-box:2376"})
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "remote daemon (gpu-box:2376)" in res.stderr
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"endpoint",
|
|
["tcp://localhost:2375", "tcp://127.0.0.1:2375", "tcp://[::1]:2375", "localhost:2375"],
|
|
)
|
|
def test_a_loopback_tcp_endpoint_is_this_machine(tmp_path: Path, endpoint: str):
|
|
"""Docker Desktop can expose its daemon on tcp://localhost:2375, and a socket can be
|
|
proxied through localhost; both are the local daemon, not a box to run this on."""
|
|
_, _, env = _setup(tmp_path, driver = False, uid = 1000)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env, extra_env = {"DOCKER_HOST": endpoint})
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "remote daemon" not in res.stderr
|
|
assert "nothing to install" in res.stdout
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"kernel", ["MINGW64_NT-10.0-22631", "MSYS_NT-10.0-22631", "CYGWIN_NT-10.0"]
|
|
)
|
|
def test_a_windows_shell_is_sent_to_docker_desktops_wsl2_backend(tmp_path: Path, kernel: str):
|
|
"""Git Bash / MSYS2 / Cygwin drive Docker Desktop, whose WSL 2 backend has the GPU
|
|
support built in. The Linux path read Desktop as "Docker Desktop for Linux" (exit 2)
|
|
and, with a plain daemon, tried apt on Windows."""
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False)
|
|
_stub(
|
|
tmp_path / "bin" / "uname", f'if [ "$1" = -s ]; then echo {kernel}; else echo x86_64; fi\n'
|
|
)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "WSL 2 backend" in res.stdout
|
|
assert "Docker Desktop for Linux" not in res.stderr
|
|
assert not any(c.startswith(("apt-get", "dnf", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
@pytest.mark.parametrize("kernel", ["MINGW64_NT-10.0-22631", "MSYS_NT-10.0"])
|
|
def test_a_windows_shell_driving_a_remote_daemon_is_sent_to_that_host(tmp_path: Path, kernel: str):
|
|
"""Same rule as on a Mac: DOCKER_HOST or a context pointing at a Linux box means that
|
|
box may need the toolkit, so the Desktop shortcut must not answer for it."""
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False)
|
|
_stub(
|
|
tmp_path / "bin" / "uname", f'if [ "$1" = -s ]; then echo {kernel}; else echo x86_64; fi\n'
|
|
)
|
|
res = _run(env, extra_env = {"DOCKER_HOST": "tcp://gpu-box:2376"})
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
assert "WSL 2 backend" not in res.stdout
|
|
res = _run(env, extra_env = {"DOCKER_CONTEXT": "remote-gpu"})
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "remote daemon (tcp://gpu-box:2376)" in res.stderr
|
|
res = _run(env, extra_env = {"DOCKER_CONTEXT": "missing-in-root-config"})
|
|
assert res.returncode == 2
|
|
assert "cannot inspect the Docker context" in res.stderr
|
|
assert not any(c.startswith(("apt-get", "dnf", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
def test_a_windows_shell_on_the_hyper_v_backend_is_told_to_switch(tmp_path: Path):
|
|
"""Docker Desktop's GPU support is the WSL 2 backend only; the Hyper-V backend runs a
|
|
LinuxKit VM no GPU reaches, so "nothing to install" there leaves --gpus failing at the
|
|
daemon. The backends are told apart by the kernel `docker info` reports."""
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False, desktop_kernel = "6.6.87.2-linuxkit")
|
|
_stub(
|
|
tmp_path / "bin" / "uname",
|
|
'if [ "$1" = -s ]; then echo MINGW64_NT-10.0-22631; else echo x86_64; fi\n',
|
|
)
|
|
res = _run(env)
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "Use the WSL 2 based engine" in res.stderr
|
|
assert "6.6.87.2-linuxkit" in res.stderr
|
|
assert "nothing to install" not in res.stdout
|
|
assert not any(c.startswith(("apt-get", "dnf", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
def test_a_windows_shell_that_cannot_reach_docker_desktop_says_so(tmp_path: Path):
|
|
"""With Desktop stopped the backend is unknowable, so neither answer can be given."""
|
|
_, log, env = _setup(tmp_path, driver = False, desktop_info_fails = True)
|
|
_stub(
|
|
tmp_path / "bin" / "uname",
|
|
'if [ "$1" = -s ]; then echo MINGW64_NT-10.0-22631; else echo x86_64; fi\n',
|
|
)
|
|
res = _run(env)
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "Docker Desktop is not running" in res.stderr
|
|
assert "WSL 2 backend" not in res.stdout
|
|
assert not any(c.startswith(("apt-get", "dnf", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
def test_a_windows_shell_on_a_non_desktop_daemon_is_sent_into_the_distro(tmp_path: Path):
|
|
"""A Docker Engine inside a WSL distro, reached through its pipe or socket, and Rancher
|
|
Desktop both answer `docker info` with something other than Docker Desktop. Neither can
|
|
be configured from a Windows shell, and neither is an error."""
|
|
_, log, env = _setup(
|
|
tmp_path,
|
|
driver = False,
|
|
desktop_os = "Ubuntu 24.04.1 LTS",
|
|
desktop_kernel = "5.15.167.4-microsoft-standard-WSL2",
|
|
)
|
|
_stub(
|
|
tmp_path / "bin" / "uname",
|
|
'if [ "$1" = -s ]; then echo MINGW64_NT-10.0-22631; else echo x86_64; fi\n',
|
|
)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "run it inside that distro" in res.stdout
|
|
assert "Ubuntu 24.04.1 LTS" in res.stdout
|
|
assert not any(c.startswith(("apt-get", "dnf", "nvidia-ctk", "systemctl")) for c in _calls(log))
|
|
|
|
|
|
def test_a_mac_never_asks_the_daemon_which_backend_it_is(tmp_path: Path):
|
|
"""No Mac takes an NVIDIA GPU whatever the daemon is, so the backend probe added for
|
|
Windows must not make macOS depend on a reachable daemon."""
|
|
_, log, env = _setup(tmp_path, driver = False, desktop_info_fails = True)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "no NVIDIA GPU" in res.stdout and "UNSLOTH_ALLOW_CPU=1" in res.stdout
|
|
assert not any(c.startswith("docker info") for c in _calls(log))
|
|
|
|
|
|
def test_a_mac_with_an_uninspectable_context_gets_the_error_not_nothing_to_install(tmp_path: Path):
|
|
"""Same rule as the endpoint check further down: a context lookup failure is an
|
|
error, not a local daemon."""
|
|
_, _, env = _setup(tmp_path, driver = False, uid = 1000)
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Darwin; else echo arm64; fi\n')
|
|
res = _run(env, extra_env = {"DOCKER_CONTEXT": "missing-in-root-config"})
|
|
assert res.returncode == 2, res.stdout + res.stderr
|
|
assert "cannot inspect the Docker context 'missing-in-root-config'" in res.stderr
|
|
assert "nothing to install" not in res.stdout
|
|
|
|
|
|
def test_the_old_driver_message_names_the_host_platform_even_without_verification(tmp_path: Path):
|
|
_, _, env = _setup(tmp_path, driver_version = "550.54.15")
|
|
_stub(tmp_path / "bin" / "uname", 'if [ "$1" = -s ]; then echo Linux; else echo aarch64; fi\n')
|
|
res = _run(env, extra_env = {"UNSLOTH_TOOLKIT_VERIFY": "0"})
|
|
assert res.returncode == 3
|
|
assert "--platform linux/arm64" in res.stderr
|
|
|
|
|
|
def test_a_configured_host_only_verifies(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, configured = True)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "already lists the nvidia runtime" in res.stdout
|
|
calls = _calls(log)
|
|
assert not any(c.startswith(("apt-get", "nvidia-ctk", "systemctl", "service")) for c in calls)
|
|
assert "docker run --rm --gpus all --platform linux/amd64 ubuntu:24.04 nvidia-smi -L" in calls
|
|
|
|
|
|
def test_docker_desktop_on_wsl_needs_nothing(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, desktop = True, driver = False, uid = 1000, wsl = True)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "WSL 2 backend" in res.stdout and "nvidia.com" in res.stdout
|
|
assert "wsl --update updates WSL itself" in res.stdout
|
|
assert [c for c in _calls(log) if not c.startswith("docker ")] == []
|
|
|
|
|
|
@pytest.mark.parametrize("systemctl", ["missing", "fails"])
|
|
def test_without_a_working_systemctl_the_service_command_restarts_docker(
|
|
tmp_path: Path, systemctl: str
|
|
):
|
|
_, log, env = _setup(tmp_path, systemctl = systemctl, wsl = True)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "service docker restart" in _calls(log)
|
|
assert "WSL 2 host" in res.stdout
|
|
|
|
|
|
def test_a_failed_verification_is_an_error_with_a_pointer(tmp_path: Path):
|
|
_, _, env = _setup(tmp_path, verify_ok = False)
|
|
res = _run(env)
|
|
assert res.returncode == 1
|
|
assert "could not see the GPU" in res.stderr
|
|
assert "troubleshooting" in res.stderr
|
|
assert "wsl --update updates WSL itself" in res.stderr
|
|
|
|
|
|
def test_verification_can_be_skipped(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, verify_ok = False)
|
|
res = _run(env, extra_env = {"UNSLOTH_TOOLKIT_VERIFY": "0"})
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert not any(c.startswith("docker run") for c in _calls(log))
|
|
|
|
|
|
def test_a_non_root_run_of_the_file_re_executes_itself_under_sudo(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, uid = 1000)
|
|
res = _run(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert [c for c in _calls(log) if not c.startswith("docker ")] == [f"sudo -E bash {INSTALLER}"]
|
|
|
|
|
|
def test_a_non_root_pipe_cannot_re_execute_and_says_so(tmp_path: Path):
|
|
_, log, env = _setup(tmp_path, uid = 1000)
|
|
res = subprocess.run(
|
|
["bash", "-s"],
|
|
input = INSTALLER.read_text(encoding = "utf-8"),
|
|
capture_output = True,
|
|
text = True,
|
|
env = env,
|
|
timeout = 60,
|
|
)
|
|
assert res.returncode == 2
|
|
assert "sudo -E bash" in res.stderr
|
|
assert [c for c in _calls(log) if not c.startswith("docker ")] == []
|
|
|
|
|
|
def _run_sh_env(
|
|
tmp_path: Path,
|
|
*,
|
|
nvidia_runtime: bool,
|
|
docker_down: bool = False,
|
|
) -> tuple[Path, Path, dict]:
|
|
bindir = tmp_path / "bin"
|
|
bindir.mkdir()
|
|
log = tmp_path / "calls.log"
|
|
argv = tmp_path / "argv"
|
|
rec = f'echo "$(basename "$0") $*" >> {log}\n'
|
|
runtimes = (
|
|
"io.containerd.runc.v2 nvidia runc" if nvidia_runtime else "io.containerd.runc.v2 runc"
|
|
)
|
|
info = (
|
|
'echo "permission denied while trying to connect to the Docker daemon socket" >&2; exit 1'
|
|
if docker_down
|
|
else f'echo " Runtimes: {runtimes}"; exit 0'
|
|
)
|
|
_stub(
|
|
bindir / "docker",
|
|
rec + f'if [ "$1" = info ]; then {info}; fi\n' f'printf "%s\\n" "$@" > {argv}\nexit 0\n',
|
|
)
|
|
_stub(bindir / "nvidia-smi", 'echo "GPU 0: NVIDIA H100 (UUID: GPU-abc)"\n')
|
|
_stub(bindir / "sudo", rec + "exit 0\n")
|
|
# Never root: as uid 0 run.sh would run the real installer on this host.
|
|
_stub(bindir / "id", "echo 1000\n")
|
|
_stub(bindir / "getent", "exit 2\n")
|
|
dev_root = tmp_path / "root"
|
|
(dev_root / "dev").mkdir(parents = True)
|
|
(dev_root / "dev" / "nvidiactl").write_text("", encoding = "utf-8")
|
|
env = dict(os.environ)
|
|
env["PATH"] = _isolated_path(tmp_path, bindir)
|
|
env["UNSLOTH_DEV_ROOT"] = str(dev_root)
|
|
env["HF_HOME"] = str(tmp_path / "hf")
|
|
env["TRITON_CACHE_DIR"] = str(tmp_path / "triton")
|
|
env.pop("UNSLOTH_INSTALL_TOOLKIT", None)
|
|
return log, argv, env
|
|
|
|
|
|
def _run_run_sh(env: dict) -> subprocess.CompletedProcess:
|
|
return subprocess.run(
|
|
["bash", str(RUN_SH), "true"],
|
|
capture_output = True,
|
|
text = True,
|
|
env = env,
|
|
stdin = subprocess.DEVNULL,
|
|
timeout = 60,
|
|
)
|
|
|
|
|
|
def test_run_sh_installs_the_toolkit_when_told_to_then_runs(tmp_path: Path):
|
|
log, argv, env = _run_sh_env(tmp_path, nvidia_runtime = False)
|
|
env["UNSLOTH_INSTALL_TOOLKIT"] = "1"
|
|
res = _run_run_sh(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert f"sudo -E bash {INSTALLER}" in _calls(log)
|
|
assert "--gpus\nall\n" in argv.read_text(encoding = "utf-8")
|
|
|
|
|
|
def test_run_sh_still_runs_the_container_when_the_installer_fails(tmp_path: Path):
|
|
"""Exit 3 (only the driver is old) and exit 1 (cancelled sudo) must not stop the docker run."""
|
|
log, argv, env = _run_sh_env(tmp_path, nvidia_runtime = False)
|
|
_stub(tmp_path / "bin" / "sudo", f'echo "$(basename "$0") $*" >> {log}\nexit 3\n')
|
|
env["UNSLOTH_INSTALL_TOOLKIT"] = "1"
|
|
res = _run_run_sh(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert f"sudo -E bash {INSTALLER}" in _calls(log)
|
|
assert argv.exists(), "docker run never happened"
|
|
|
|
|
|
def test_run_sh_without_a_terminal_prints_the_one_liner_and_still_runs(tmp_path: Path):
|
|
log, argv, env = _run_sh_env(tmp_path, nvidia_runtime = False)
|
|
res = _run_run_sh(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "-o install_nvidia_toolkit.sh && sudo -E bash install_nvidia_toolkit.sh" in res.stderr
|
|
assert not any(c.startswith("sudo") for c in _calls(log))
|
|
assert argv.exists()
|
|
|
|
|
|
def test_run_sh_does_not_offer_an_install_when_docker_itself_is_unreachable(tmp_path: Path):
|
|
log, argv, env = _run_sh_env(tmp_path, nvidia_runtime = False, docker_down = True)
|
|
env["UNSLOTH_INSTALL_TOOLKIT"] = "1"
|
|
res = _run_run_sh(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "permission denied" in res.stderr and "docker group" in res.stderr
|
|
assert "Container Toolkit is not set up" not in res.stderr
|
|
assert not any(c.startswith("sudo") for c in _calls(log))
|
|
assert argv.exists()
|
|
|
|
|
|
def test_run_sh_uses_no_scratch_file_for_the_daemon_probe():
|
|
text = RUN_SH.read_text(encoding = "utf-8")
|
|
assert "unsloth-docker-info" not in text and "mktemp" not in text
|
|
assert 'DOCKER_INFO="$(docker info 2>&1)"' in text
|
|
|
|
|
|
def test_run_sh_is_quiet_when_the_runtime_is_present(tmp_path: Path):
|
|
_, _, env = _run_sh_env(tmp_path, nvidia_runtime = True)
|
|
res = _run_run_sh(env)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
assert "Container Toolkit" not in res.stderr
|
|
|
|
|
|
def test_the_docs_point_at_the_installer():
|
|
for doc in (REPO_ROOT / "docker" / "DOCKERHUB.md", REPO_ROOT / "README.md"):
|
|
text = doc.read_text(encoding = "utf-8")
|
|
assert (
|
|
"install_nvidia_toolkit.sh -o install_nvidia_toolkit.sh && sudo -E bash install_nvidia_toolkit.sh"
|
|
in text
|
|
), doc
|
|
assert "| sudo" not in text, "a pipe into bash masks a failed download"
|
|
|
|
assert "Docker Desktop" in (REPO_ROOT / "docker" / "DOCKERHUB.md").read_text(encoding = "utf-8")
|