"""Image import and the shared BPRT folder. Run from the webapp folder: PYTHONPATH=.: 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()