fix(smolmachines): pipe tar stdout via PIPE not file fd
smolvm machine exec requires stdout to be a pipe, not a regular file descriptor. Passing stdout=file caused smolvm to return non-zero with no stderr (the error was silently swallowed or went to the regular-file fd instead of reaching us). Switch _snapshot_running_vm to a new _exec_tar_to_file helper that uses Popen with stdout=PIPE and streams the tar to disk via shutil.copyfileobj. A background thread drains stderr concurrently to prevent deadlock when the stderr pipe buffer fills while we are writing stdout data.
This commit is contained in:
@@ -9,8 +9,10 @@ running throughout."""
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .. import ActiveAgent
|
from .. import ActiveAgent
|
||||||
@@ -56,29 +58,7 @@ def _snapshot_running_vm(machine: str, image_ref: str, binary: Path) -> None:
|
|||||||
rootfs_tar = tmp_path / "rootfs.tar"
|
rootfs_tar = tmp_path / "rootfs.tar"
|
||||||
dockerfile = tmp_path / "Dockerfile"
|
dockerfile = tmp_path / "Dockerfile"
|
||||||
|
|
||||||
with open(rootfs_tar, "wb") as tar_out:
|
_exec_tar_to_file(machine, rootfs_tar)
|
||||||
result = subprocess.run(
|
|
||||||
[
|
|
||||||
"smolvm", "machine", "exec",
|
|
||||||
"--name", machine, "--",
|
|
||||||
"tar", "--create",
|
|
||||||
"--exclude=./proc",
|
|
||||||
"--exclude=./sys",
|
|
||||||
"--exclude=./dev",
|
|
||||||
"--exclude=./run",
|
|
||||||
"--file=-",
|
|
||||||
"--directory=/",
|
|
||||||
".",
|
|
||||||
],
|
|
||||||
stdout=tar_out,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
check=False,
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
die(
|
|
||||||
f"smolvm exec tar {machine!r} failed: "
|
|
||||||
f"{(result.stderr or b'').decode().strip() or '<no stderr>'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
dockerfile.write_text(
|
dockerfile.write_text(
|
||||||
"FROM scratch\n"
|
"FROM scratch\n"
|
||||||
@@ -99,3 +79,50 @@ def _snapshot_running_vm(machine: str, image_ref: str, binary: Path) -> None:
|
|||||||
pack_create(pack_ref, binary)
|
pack_create(pack_ref, binary)
|
||||||
finally:
|
finally:
|
||||||
image_tarball.unlink(missing_ok=True)
|
image_tarball.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _exec_tar_to_file(machine: str, dest: Path) -> None:
|
||||||
|
"""Stream the VM root filesystem as a tar archive to `dest`.
|
||||||
|
|
||||||
|
smolvm machine exec requires stdout to be a pipe, not a regular file.
|
||||||
|
We use Popen with stdout=PIPE and drain stderr in a background thread
|
||||||
|
to avoid deadlock if either buffer fills while we're writing the other."""
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
[
|
||||||
|
"smolvm", "machine", "exec",
|
||||||
|
"--name", machine, "--",
|
||||||
|
"tar", "--create",
|
||||||
|
"--exclude=./proc",
|
||||||
|
"--exclude=./sys",
|
||||||
|
"--exclude=./dev",
|
||||||
|
"--exclude=./run",
|
||||||
|
"--file=-",
|
||||||
|
"--directory=/",
|
||||||
|
".",
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
stderr_chunks: list[bytes] = []
|
||||||
|
|
||||||
|
def _drain_stderr() -> None:
|
||||||
|
assert proc.stderr is not None
|
||||||
|
stderr_chunks.append(proc.stderr.read())
|
||||||
|
|
||||||
|
t = threading.Thread(target=_drain_stderr, daemon=True)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
assert proc.stdout is not None
|
||||||
|
with open(dest, "wb") as out:
|
||||||
|
shutil.copyfileobj(proc.stdout, out, length=65536)
|
||||||
|
|
||||||
|
t.join()
|
||||||
|
returncode = proc.wait()
|
||||||
|
stderr = b"".join(stderr_chunks).decode(errors="replace").strip()
|
||||||
|
|
||||||
|
if returncode != 0:
|
||||||
|
die(
|
||||||
|
f"smolvm exec tar {machine!r} failed: "
|
||||||
|
f"{stderr or '<no stderr>'}"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user