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.
147 lines
6.9 KiB
Python
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()
|