Files
pxe-server/webapp/tests/test_bprt_import.py
cproudlock d63cc5761b Image import keeps the shared BPRT link and seeds BPRT on a fresh box
An MCL import merged Deploy/Applications with rsync -a, which replaced each
image's BPRT symlink with a plain copy of the vendor folder. That copy has
no site overlay, so standard/engineer enrollment lost
Register-BulkEnrollTask-Standard.ps1 (specialize Order 39).

Now Deploy/Applications is imported first and without BPRT:
- where _shared/Applications/BPRT exists it is left alone (the BPRT page
  keeps it current) and the image link is put back;
- on a box with none yet, the imported BPRT seeds it: vendor
  Select-PCConfig.ps1 kept as -vendor, our overlay added, BOM fix applied;
- the import refuses before anything moves when that folder would not work
  (vendor scripts missing, or no overlay copy to add).

The two Register scripts join Select-PCConfig.ps1 in the repo. The playbook
already syncs shopfloor-setup to the enrollment share, so
config.BPRT_OVERLAY_DIR finds all three there; overlay_sources now falls
back to it for every overlay file, not only the shim.
2026-10-16 14:56:33 -04:00

147 lines
6.9 KiB
Python

"""Image import and the shared BPRT folder.
Run from the webapp folder:
PYTHONPATH=.:<deps> python -m unittest tests.test_bprt_import -v
An MCL import carries Deploy/Applications/BPRT. rsync used to replace each
image's BPRT link with that plain vendor copy, which has no site overlay, so
standard/engineer enrollment lost Register-BulkEnrollTask-Standard.ps1. Now
the import seeds _shared BPRT on a fresh box, leaves an existing one alone,
and always restores the link. Every config path points into a temp folder.
"""
import os
import shutil
import sys
import tempfile
import unittest
from unittest import mock
_T = tempfile.mkdtemp(prefix="pxe-bprt-import-test-")
for _k, _sub in {"SAMBA_SHARE": "samba", "ENROLLMENT_SHARE": "enr", "UPDATES_DIR": "upd",
"UPLOAD_DIR": "upl"}.items():
os.environ.setdefault(_k, os.path.join(_T, _sub))
os.makedirs(os.environ[_k], exist_ok=True)
os.environ.setdefault("AUDIT_LOG", os.path.join(_T, "audit_log"))
WEBAPP = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
REPO_OVERLAY = os.path.join(WEBAPP, "..", "playbook", "shopfloor-setup", "BPRT")
sys.path.insert(0, WEBAPP)
import config # noqa: E402
from services import bprt_sync as BS # noqa: E402
from services import deploy # noqa: E402
VENDOR_SELECT = b"# vendor dialog\r\nShow-Dialog\r\n"
def write(path, data=b"x"):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "wb") as f:
f.write(data)
def vendor_bprt(root, version=b"v1", skip=()):
for name in BS.REQUIRED:
if name in skip:
continue
write(os.path.join(root, name), VENDOR_SELECT if name == "Select-PCConfig.ps1" else version)
write(os.path.join(root, "Translations", "de.psd1"), b"@{}")
def read(path):
with open(path, "rb") as f:
return f.read()
class BprtImport(unittest.TestCase):
def setUp(self):
for d in (config.SAMBA_SHARE, config.UPLOAD_DIR, config.ENROLLMENT_SHARE, config.UPDATES_DIR):
shutil.rmtree(d, ignore_errors=True)
os.makedirs(d)
shutil.copytree(REPO_OVERLAY, config.BPRT_OVERLAY_DIR)
self.src = os.path.join(config.UPLOAD_DIR, "Deploy")
vendor_bprt(os.path.join(self.src, "Applications", "BPRT"))
write(os.path.join(self.src, "Applications", "extra", "app.msi"), b"msi")
write(os.path.join(self.src, "Control", "OperatingSystem.json"), b"{}")
self.dest = os.path.join(config.SAMBA_SHARE, "gea-standard", "Deploy")
self.live = BS.bprt_dir()
self.link = os.path.join(self.dest, "Applications", "BPRT")
def assert_linked(self):
self.assertTrue(os.path.islink(self.link), "image BPRT is not a link")
self.assertEqual(os.path.realpath(self.link), os.path.realpath(self.live))
def test_fresh_box_seeds_shared_bprt_with_overlay(self):
deploy.import_deploy(self.src, self.dest, "gea-standard")
self.assertTrue(os.path.isdir(self.live) and not os.path.islink(self.live))
self.assertTrue(BS.is_shim(os.path.join(self.live, "Select-PCConfig.ps1")))
self.assertEqual(read(os.path.join(self.live, "Select-PCConfig-vendor.ps1")), VENDOR_SELECT)
for name in BS.OVERLAY:
self.assertEqual(read(os.path.join(self.live, name)),
read(os.path.join(REPO_OVERLAY, name)), name)
self.assertTrue(os.path.isfile(os.path.join(self.live, "Translations", "de.psd1")))
self.assert_linked()
self.assertEqual(read(os.path.join(self.dest, "Applications", "extra", "app.msi")), b"msi")
self.assertFalse(os.path.exists(BS.new_dir()))
def test_existing_bprt_is_kept_and_link_restored(self):
vendor_bprt(self.live, version=b"live")
shutil.copy2(os.path.join(REPO_OVERLAY, "Select-PCConfig.ps1"),
os.path.join(self.live, "Select-PCConfig.ps1"))
# what the old import left: a plain copy in place of the link
vendor_bprt(self.link, version=b"stale copy")
deploy.import_deploy(self.src, self.dest, "gea-standard")
self.assertEqual(read(os.path.join(self.live, "WCD-Helpers.ps1")), b"live")
self.assertTrue(BS.is_shim(os.path.join(self.live, "Select-PCConfig.ps1")))
self.assert_linked()
def test_differently_cased_copy_is_replaced(self):
vendor_bprt(self.live, version=b"live")
vendor_bprt(os.path.join(self.dest, "Applications", "bprt"), version=b"stale")
deploy.import_deploy(self.src, self.dest, "gea-standard")
self.assertEqual(sorted(n for n in os.listdir(os.path.join(self.dest, "Applications"))
if n.lower() == "bprt"), ["BPRT"])
self.assert_linked()
def test_fresh_box_without_overlay_refuses_before_anything_moves(self):
empty = os.path.join(_T, "no-overlay")
os.makedirs(empty, exist_ok=True)
with mock.patch.object(BS, "overlay_fallbacks", return_value=[empty]):
with self.assertRaisesRegex(RuntimeError, "Register-BulkEnrollTask-Standard.ps1"):
deploy.import_deploy(self.src, self.dest, "gea-standard", move=True)
self.assertFalse(os.path.lexists(self.live))
self.assertFalse(os.path.exists(os.path.join(self.dest, "Applications")))
self.assertFalse(os.path.exists(os.path.join(self.dest, "Control")))
self.assertTrue(os.path.isfile(os.path.join(self.src, "Applications", "extra", "app.msi")))
def test_incomplete_vendor_bprt_refuses(self):
shutil.rmtree(os.path.join(self.src, "Applications", "BPRT"))
vendor_bprt(os.path.join(self.src, "Applications", "BPRT"), skip=("WCD-Helpers.ps1",))
with self.assertRaisesRegex(RuntimeError, "WCD-Helpers.ps1"):
deploy.import_deploy(self.src, self.dest, "gea-standard")
self.assertFalse(os.path.lexists(self.live))
def test_move_clears_the_upload_bprt(self):
deploy.import_deploy(self.src, self.dest, "gea-standard", move=True)
self.assertFalse(os.path.exists(os.path.join(self.src, "Applications")))
self.assert_linked()
def test_overlay_sources_fill_in_register_scripts(self):
vendor_bprt(self.live)
files, _ = BS.walk(self.live)
srcs = BS.overlay_sources(self.live, files)
self.assertEqual(srcs["Register-BulkEnrollTask-Standard.ps1"],
os.path.join(config.BPRT_OVERLAY_DIR, "Register-BulkEnrollTask-Standard.ps1"))
self.assertEqual(os.path.dirname(srcs["Select-PCConfig.ps1"]), config.BPRT_OVERLAY_DIR)
def test_vendor_select_in_overlay_dir_is_not_our_shim(self):
os.remove(os.path.join(config.BPRT_OVERLAY_DIR, "Select-PCConfig.ps1"))
write(os.path.join(config.BPRT_OVERLAY_DIR, "Select-PCConfig.ps1"), VENDOR_SELECT)
with mock.patch.object(BS, "overlay_fallbacks", return_value=[config.BPRT_OVERLAY_DIR]):
with self.assertRaisesRegex(RuntimeError, "Select-PCConfig.ps1"):
deploy.import_deploy(self.src, self.dest, "gea-standard")
if __name__ == "__main__":
unittest.main()