test(vm): keep ISO storage isolation in fixtures

This commit is contained in:
martino
2026-10-16 16:28:23 +02:00
parent f07dbfe10f
commit b55eca753d
2 changed files with 21 additions and 9 deletions
+19 -7
View File
@@ -1,8 +1,9 @@
"""The VM wizard must not accept failed ISO downloads as installation media. """The VM wizard must not accept failed ISO downloads as installation media.
Run its real functions against a loopback HTTP fixture and stub only external Run its function logic against a loopback HTTP fixture. Redirect the fixed
Proxmox/UI services. No product script is sourced, no guest operation runs, native ISO directory only in the loaded test copy and stub Proxmox/UI services.
and no public network or real ISO storage is accessed. No product script is sourced, no guest operation runs, and no public network
or real ISO storage is accessed.
""" """
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import os import os
@@ -97,10 +98,15 @@ class VmIsoDownloadTests(unittest.TestCase):
) )
wrapper.chmod(0o755) wrapper.chmod(0o755)
def run_wizard(self, route="/success.iso", windows=False, overwrite=True, url=None): def run_wizard(self, route="/success.iso", windows=False, overwrite=True, url=None,
caller_iso_dir=None):
source = CREATOR.read_text() source = CREATOR.read_text()
functions = "\n\n".join(re.findall(r"^function \w+\(\) \{\n.*?^\}", source, re.M | re.S)) functions = "\n\n".join(re.findall(r"^function \w+\(\) \{\n.*?^\}", source, re.M | re.S))
self.assertIn("function create_vm()", functions) self.assertIn("function create_vm()", functions)
# Only relocate this external filesystem anchor. Keep all predicates,
# transfers and promotion logic unchanged, without a production hook.
self.assertIn("/var/lib/vz/template/iso", functions)
functions = functions.replace("/var/lib/vz/template/iso", "${FIXTURE_DIR}")
prelude = r''' prelude = r'''
translate() { printf '%s' "$1"; } translate() { printf '%s' "$1"; }
msg_ok() { printf 'OK:%s\n' "$*"; } msg_ok() { printf 'OK:%s\n' "$*"; }
@@ -133,12 +139,15 @@ class VmIsoDownloadTests(unittest.TestCase):
''' '''
env = dict(os.environ, PATH=str(self.bin) + os.pathsep + os.environ["PATH"], env = dict(os.environ, PATH=str(self.bin) + os.pathsep + os.environ["PATH"],
ISO_PATH=str(self.iso), ISO_URL=url if url is not None else self.http + route, ISO_PATH=str(self.iso), ISO_URL=url if url is not None else self.http + route,
ISO_VOLID="local:iso/installation media.iso", ISO_DIR=str(self.directory), ISO_VOLID="local:iso/installation media.iso",
OS_TYPE="2" if windows else "3", OS_TYPE="2" if windows else "3",
QM_LOG=str(self.qm_log), WGET_LOG=str(self.wget_log), QM_LOG=str(self.qm_log), WGET_LOG=str(self.wget_log),
DIALOG_ONCE=str(self.directory / "dialog-once"), FIXTURE_DIR=str(self.directory), DIALOG_ONCE=str(self.directory / "dialog-once"), FIXTURE_DIR=str(self.directory),
OVERWRITE_STATUS="0" if overwrite else "1", HTTP_FIXTURE=self.http, OVERWRITE_STATUS="0" if overwrite else "1", HTTP_FIXTURE=self.http,
VIRTIO_FIXTURE_URL=self.http + route) VIRTIO_FIXTURE_URL=self.http + route)
env.pop("ISO_DIR", None)
if caller_iso_dir is not None:
env["ISO_DIR"] = str(caller_iso_dir)
return subprocess.run(["bash", "--noprofile", "--norc", "-c", functions + prelude], return subprocess.run(["bash", "--noprofile", "--norc", "-c", functions + prelude],
env=env, stdin=subprocess.DEVNULL, capture_output=True, env=env, stdin=subprocess.DEVNULL, capture_output=True,
text=True, timeout=15) text=True, timeout=15)
@@ -166,14 +175,17 @@ class VmIsoDownloadTests(unittest.TestCase):
self.assertEqual(self.iso.read_bytes(), PAYLOAD) self.assertEqual(self.iso.read_bytes(), PAYLOAD)
self.assertIn("create ", self.qm_calls()) self.assertIn("create ", self.qm_calls())
def test_virtio_download_uses_the_configured_iso_directory(self): def test_virtio_download_keeps_native_storage_despite_caller_environment(self):
self.iso.write_bytes(PAYLOAD) self.iso.write_bytes(PAYLOAD)
result = self.run_wizard(windows=True) unrelated = self.directory / "unrelated directory"
unrelated.mkdir()
result = self.run_wizard(windows=True, caller_iso_dir=unrelated)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr) self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
driver = self.directory / "virtio-win.iso" driver = self.directory / "virtio-win.iso"
self.assertTrue(driver.is_file(), result.stdout + result.stderr) self.assertTrue(driver.is_file(), result.stdout + result.stderr)
self.assertEqual(driver.read_bytes(), PAYLOAD) self.assertEqual(driver.read_bytes(), PAYLOAD)
self.assertIn("-ide3 ", self.qm_calls()) self.assertIn("-ide3 ", self.qm_calls())
self.assertEqual(list(unrelated.iterdir()), [])
def test_failed_virtio_fetch_is_not_selected_for_attachment(self): def test_failed_virtio_fetch_is_not_selected_for_attachment(self):
self.iso.write_bytes(PAYLOAD) self.iso.write_bytes(PAYLOAD)
+2 -2
View File
@@ -285,7 +285,7 @@ function create_vm() {
local BOOT_ORDER="" local BOOT_ORDER=""
local DISK_INFO="" local DISK_INFO=""
local DISK_INDEX=0 local DISK_INDEX=0
local ISO_DIR="${ISO_DIR:-/var/lib/vz/template/iso}" local ISO_DIR="/var/lib/vz/template/iso"
if [[ -n "$ISO_PATH" && -n "$ISO_URL" ]] && [[ ! -f "$ISO_PATH" || ! -s "$ISO_PATH" ]]; then if [[ -n "$ISO_PATH" && -n "$ISO_URL" ]] && [[ ! -f "$ISO_PATH" || ! -s "$ISO_PATH" ]]; then
@@ -657,7 +657,7 @@ fi
if [[ "$OS_TYPE" == "2" ]]; then if [[ "$OS_TYPE" == "2" ]]; then
local VIRTIO_DIR="$ISO_DIR" local VIRTIO_DIR="/var/lib/vz/template/iso"
local VIRTIO_SELECTED="" local VIRTIO_SELECTED=""
local VIRTIO_VOLID="" local VIRTIO_VOLID=""
local VIRTIO_DOWNLOAD_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" local VIRTIO_DOWNLOAD_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso"