From 00575b6dc20c9a161c9aa8c35e3717bb74c7440c Mon Sep 17 00:00:00 2001 From: Jessica Castelino Date: Fri, 15 Dec 2023 22:02:17 +0000 Subject: [PATCH] Patch extraction and validation enhancements This commit updates patch extraction process to read and untar specific files. Additionally, it updates the process used to extract sw_version from deeply nested tar archives of a patch file. Test Plan: [PASS] Ran below command in DC env that invokes this code path sw-patch --os-region-name SystemController upload .patch [PASS] Uploaded an in-service and RR patch Story: 2010993 Task: 49289 Change-Id: I7ada7b55f458c50ed3bf51e66841cc49592f2f71 Signed-off-by: Jessica Castelino --- .../cgcs-patch/cgcs_patch/patch_functions.py | 3 +-- software/software/software_functions.py | 27 ++++++++++--------- .../cgcs-patch/cgcs_patch/patch_functions.py | 27 ++++++++++--------- .../cgcs_patch/tests/test_patch_functions.py | 12 +++++++-- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/cgcs-patch/cgcs-patch/cgcs_patch/patch_functions.py b/cgcs-patch/cgcs-patch/cgcs_patch/patch_functions.py index d14eb13d..f8a019eb 100644 --- a/cgcs-patch/cgcs-patch/cgcs_patch/patch_functions.py +++ b/cgcs-patch/cgcs-patch/cgcs_patch/patch_functions.py @@ -202,8 +202,7 @@ def parse_pkgver(pkgver): def get_release_from_patch(patchfile): rel = "" try: - cmd = "tar xf %s -O metadata.tar | tar x -O" % patchfile - metadata_str = subprocess.check_output(cmd, shell=True) + metadata_str = subprocess.check_output(['tar', '--to-command=tar -xO', '-xf', patchfile, 'metadata.tar']) root = ElementTree.fromstring(metadata_str) # Extract release version rel = root.findtext('sw_version') diff --git a/software/software/software_functions.py b/software/software/software_functions.py index 71f6e6ad..0f9bd408 100644 --- a/software/software/software_functions.py +++ b/software/software/software_functions.py @@ -157,8 +157,7 @@ def write_xml_file(top, def get_release_from_patch(patchfile): rel = "" try: - cmd = "tar xf %s -O metadata.tar | tar x -O" % patchfile - metadata_str = subprocess.check_output(cmd, shell=True) + metadata_str = subprocess.check_output(['tar', '--to-command=tar -xO', '-xf', patchfile, 'metadata.tar']) root = ElementTree.fromstring(metadata_str) # Extract release version rel = root.findtext('sw_version') @@ -625,22 +624,21 @@ class PatchFile(object): # Open the patch file and extract the contents to the current dir tar = tarfile.open(path, "r:gz") - filelist = [] - for f in tar.getmembers(): - filelist.append(f.name) - - if detached_signature_file not in filelist: - msg = "Patch not signed" + tar.extract("signature") + try: + tar.extract(detached_signature_file) + except KeyError: + msg = "Patch has not been signed" LOG.warning(msg) - for f in filelist: - tar.extract(f) - # Filelist used for signature validation and verification sig_filelist = ["metadata.tar", "software.tar"] - if "semantics.tar" in filelist: + if "semantics.tar" in [f.name for f in tar.getmembers()]: sig_filelist.append("semantics.tar") + for f in sig_filelist: + tar.extract(f) + # Verify the data integrity signature first sigfile = open("signature", "r") sig = int(sigfile.read(), 16) @@ -676,6 +674,11 @@ class PatchFile(object): LOG.error(msg) raise ReleaseValidationFailure(msg) + # Restart script + for f in tar.getmembers(): + if f.name not in sig_filelist: + tar.extract(f) + tar = tarfile.open("metadata.tar") tar.extractall() diff --git a/sw-patch/cgcs-patch/cgcs_patch/patch_functions.py b/sw-patch/cgcs-patch/cgcs_patch/patch_functions.py index 1741790a..f89d7997 100644 --- a/sw-patch/cgcs-patch/cgcs_patch/patch_functions.py +++ b/sw-patch/cgcs-patch/cgcs_patch/patch_functions.py @@ -157,8 +157,7 @@ def write_xml_file(top, def get_release_from_patch(patchfile): rel = "" try: - cmd = "tar xf %s -O metadata.tar | tar x -O" % patchfile - metadata_str = subprocess.check_output(cmd, shell=True) + metadata_str = subprocess.check_output(['tar', '--to-command=tar -xO', '-xf', patchfile, 'metadata.tar']) root = ElementTree.fromstring(metadata_str) # Extract release version rel = root.findtext('sw_version') @@ -618,22 +617,21 @@ class PatchFile(object): # Open the patch file and extract the contents to the current dir tar = tarfile.open(path, "r:gz") - filelist = [] - for f in tar.getmembers(): - filelist.append(f.name) - - if detached_signature_file not in filelist: - msg = "Patch not signed" + tar.extract("signature") + try: + tar.extract(detached_signature_file) + except KeyError: + msg = "Patch has not been signed" LOG.warning(msg) - for f in filelist: - tar.extract(f) - # Filelist used for signature validation and verification sig_filelist = ["metadata.tar", "software.tar"] - if "semantics.tar" in filelist: + if "semantics.tar" in [f.name for f in tar.getmembers()]: sig_filelist.append("semantics.tar") + for f in sig_filelist: + tar.extract(f) + # Verify the data integrity signature first sigfile = open("signature", "r") sig = int(sigfile.read(), 16) @@ -669,6 +667,11 @@ class PatchFile(object): LOG.error(msg) raise PatchValidationFailure(msg) + # Restart script + for f in tar.getmembers(): + if f.name not in sig_filelist: + tar.extract(f) + tar = tarfile.open("metadata.tar") tar.extractall() diff --git a/sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_functions.py b/sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_functions.py index 613f807f..da4a2209 100644 --- a/sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_functions.py +++ b/sw-patch/cgcs-patch/cgcs_patch/tests/test_patch_functions.py @@ -106,6 +106,14 @@ class FakeTar(object): return True +class NoSignatureTar(FakeTar): + def extract(self, filename): + if filename == "signature.v2": # pylint: disable=no-else-raise + raise KeyError("Signature doesn't exist") + else: + return filename + + class CgcsPatchFunctionsTestCase(testtools.TestCase): def create_element_tree_from_dict(self, tree_root, dict_obj): @@ -208,9 +216,9 @@ class CgcsPatchFunctionsTestCase(testtools.TestCase): _mock_log_warning, _mock_open): test_obj = PatchFile() - _mock_open.return_value = FakeTar(["file1"]) + _mock_open.return_value = NoSignatureTar(["file1"]) self.assertRaises(PatchValidationFailure, test_obj.read_patch, "fake_path") - _mock_log_warning.assert_any_call('Patch not signed') + _mock_log_warning.assert_any_call('Patch has not been signed') _mock_log_error.assert_any_call('Patch failed verification') @mock.patch.object(tarfile, "open")