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.
This commit is contained in:
146
webapp/tests/test_bprt_import.py
Normal file
146
webapp/tests/test_bprt_import.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user