clan-cli: Fix tmpdir leak and fix tests/temporary_dir inconsistencies
This commit is contained in:
parent
0676bf7283
commit
e6ad0cfbc1
@ -3,6 +3,7 @@ import importlib
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from contextlib import ExitStack
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
@ -108,86 +109,87 @@ def run_vm(
|
|||||||
socketdir: Path | None = None,
|
socketdir: Path | None = None,
|
||||||
nix_options: list[str] = [],
|
nix_options: list[str] = [],
|
||||||
) -> None:
|
) -> None:
|
||||||
machine = Machine(vm.machine_name, vm.flake_url)
|
with ExitStack() as stack:
|
||||||
log.debug(f"Creating VM for {machine}")
|
machine = Machine(vm.machine_name, vm.flake_url)
|
||||||
|
log.debug(f"Creating VM for {machine}")
|
||||||
|
|
||||||
# store the temporary rootfs inside XDG_CACHE_HOME on the host
|
# store the temporary rootfs inside XDG_CACHE_HOME on the host
|
||||||
# otherwise, when using /tmp, we risk running out of memory
|
# otherwise, when using /tmp, we risk running out of memory
|
||||||
cache = user_cache_dir() / "clan"
|
cache = user_cache_dir() / "clan"
|
||||||
cache.mkdir(exist_ok=True)
|
cache.mkdir(exist_ok=True)
|
||||||
|
|
||||||
if cachedir is None:
|
if cachedir is None:
|
||||||
cache_tmp = TemporaryDirectory(dir=cache)
|
cache_tmp = stack.enter_context(TemporaryDirectory(dir=cache))
|
||||||
cachedir = Path(cache_tmp.name)
|
cachedir = Path(cache_tmp)
|
||||||
|
|
||||||
if socketdir is None:
|
if socketdir is None:
|
||||||
socket_tmp = TemporaryDirectory()
|
socket_tmp = stack.enter_context(TemporaryDirectory())
|
||||||
socketdir = Path(socket_tmp.name)
|
socketdir = Path(socket_tmp)
|
||||||
|
|
||||||
# TODO: We should get this from the vm argument
|
# TODO: We should get this from the vm argument
|
||||||
nixos_config = build_vm(machine, cachedir, nix_options)
|
nixos_config = build_vm(machine, cachedir, nix_options)
|
||||||
|
|
||||||
state_dir = vm_state_dir(str(vm.flake_url), machine.name)
|
state_dir = vm_state_dir(str(vm.flake_url), machine.name)
|
||||||
state_dir.mkdir(parents=True, exist_ok=True)
|
state_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# specify socket files for qmp and qga
|
# specify socket files for qmp and qga
|
||||||
qmp_socket_file = socketdir / "qmp.sock"
|
qmp_socket_file = socketdir / "qmp.sock"
|
||||||
qga_socket_file = socketdir / "qga.sock"
|
qga_socket_file = socketdir / "qga.sock"
|
||||||
# Create symlinks to the qmp/qga sockets to be able to find them later.
|
# Create symlinks to the qmp/qga sockets to be able to find them later.
|
||||||
# This indirection is needed because we cannot put the sockets directly
|
# This indirection is needed because we cannot put the sockets directly
|
||||||
# in the state_dir.
|
# in the state_dir.
|
||||||
# The reason is, qemu has a length limit of 108 bytes for the qmp socket
|
# The reason is, qemu has a length limit of 108 bytes for the qmp socket
|
||||||
# path which is violated easily.
|
# path which is violated easily.
|
||||||
qmp_link = state_dir / "qmp.sock"
|
qmp_link = state_dir / "qmp.sock"
|
||||||
if os.path.lexists(qmp_link):
|
if os.path.lexists(qmp_link):
|
||||||
qmp_link.unlink()
|
qmp_link.unlink()
|
||||||
qmp_link.symlink_to(qmp_socket_file)
|
qmp_link.symlink_to(qmp_socket_file)
|
||||||
|
|
||||||
qga_link = state_dir / "qga.sock"
|
qga_link = state_dir / "qga.sock"
|
||||||
if os.path.lexists(qga_link):
|
if os.path.lexists(qga_link):
|
||||||
qga_link.unlink()
|
qga_link.unlink()
|
||||||
qga_link.symlink_to(qga_socket_file)
|
qga_link.symlink_to(qga_socket_file)
|
||||||
|
|
||||||
rootfs_img = prepare_disk(cachedir)
|
rootfs_img = prepare_disk(cachedir)
|
||||||
state_img = state_dir / "state.qcow2"
|
state_img = state_dir / "state.qcow2"
|
||||||
if not state_img.exists():
|
if not state_img.exists():
|
||||||
state_img = prepare_disk(
|
state_img = prepare_disk(
|
||||||
directory=state_dir,
|
directory=state_dir,
|
||||||
file_name="state.qcow2",
|
file_name="state.qcow2",
|
||||||
size="50G",
|
size="50G",
|
||||||
)
|
)
|
||||||
virtiofsd_socket = socketdir / "virtiofsd.sock"
|
virtiofsd_socket = socketdir / "virtiofsd.sock"
|
||||||
qemu_cmd = qemu_command(
|
qemu_cmd = qemu_command(
|
||||||
vm,
|
vm,
|
||||||
nixos_config,
|
nixos_config,
|
||||||
secrets_dir=Path(nixos_config["secrets_dir"]),
|
secrets_dir=Path(nixos_config["secrets_dir"]),
|
||||||
rootfs_img=rootfs_img,
|
rootfs_img=rootfs_img,
|
||||||
state_img=state_img,
|
state_img=state_img,
|
||||||
virtiofsd_socket=virtiofsd_socket,
|
virtiofsd_socket=virtiofsd_socket,
|
||||||
qmp_socket_file=qmp_socket_file,
|
qmp_socket_file=qmp_socket_file,
|
||||||
qga_socket_file=qga_socket_file,
|
qga_socket_file=qga_socket_file,
|
||||||
)
|
|
||||||
|
|
||||||
packages = ["nixpkgs#qemu"]
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
|
||||||
if vm.graphics and not vm.waypipe:
|
|
||||||
packages.append("nixpkgs#virt-viewer")
|
|
||||||
remote_viewer_mimetypes = module_root() / "vms" / "mimetypes"
|
|
||||||
env["XDG_DATA_DIRS"] = (
|
|
||||||
f"{remote_viewer_mimetypes}:{env.get('XDG_DATA_DIRS', '')}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with (
|
packages = ["nixpkgs#qemu"]
|
||||||
start_waypipe(qemu_cmd.vsock_cid, f"[{vm.machine_name}] "),
|
|
||||||
start_virtiofsd(virtiofsd_socket),
|
env = os.environ.copy()
|
||||||
):
|
if vm.graphics and not vm.waypipe:
|
||||||
run(
|
packages.append("nixpkgs#virt-viewer")
|
||||||
nix_shell(packages, qemu_cmd.args),
|
remote_viewer_mimetypes = module_root() / "vms" / "mimetypes"
|
||||||
env=env,
|
env["XDG_DATA_DIRS"] = (
|
||||||
log=Log.BOTH,
|
f"{remote_viewer_mimetypes}:{env.get('XDG_DATA_DIRS', '')}"
|
||||||
error_msg=f"Could not start vm {machine}",
|
)
|
||||||
)
|
|
||||||
|
with (
|
||||||
|
start_waypipe(qemu_cmd.vsock_cid, f"[{vm.machine_name}] "),
|
||||||
|
start_virtiofsd(virtiofsd_socket),
|
||||||
|
):
|
||||||
|
run(
|
||||||
|
nix_shell(packages, qemu_cmd.args),
|
||||||
|
env=env,
|
||||||
|
log=Log.BOTH,
|
||||||
|
error_msg=f"Could not start vm {machine}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -16,6 +16,7 @@ def temporary_home(monkeypatch: pytest.MonkeyPatch) -> Iterator[Path]:
|
|||||||
path = Path(env_dir).resolve()
|
path = Path(env_dir).resolve()
|
||||||
log.debug("Temp HOME directory: %s", str(path))
|
log.debug("Temp HOME directory: %s", str(path))
|
||||||
monkeypatch.setenv("HOME", str(path))
|
monkeypatch.setenv("HOME", str(path))
|
||||||
|
monkeypatch.setenv("XDG_CONFIG_HOME", str(path / ".config"))
|
||||||
monkeypatch.chdir(str(path))
|
monkeypatch.chdir(str(path))
|
||||||
yield path
|
yield path
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user