Fix: Patch Apply Order

When we apply a patch, we want the patch apply order to be
such that the patch with the lowest dependency is applied first.
For example:
P2 requires P1
P3 requires P2
P4 requires P3
Apply order: [P1, P2, P3, P4]
Since P1 has the lowest dependency (None) followed by
P2 (P1) followed by P3 (P2, P1) followed by P4 (P3, P2, P1).

Test Plan:
Tested different scenarios with unit test cases

Closes-Bug: 2003823
Signed-off-by: Jessica Castelino <jessica.castelino@windriver.com>
Change-Id: Ic57d9e182179b1aa7c26028e941ede85b4b505f7
This commit is contained in:
Jessica Castelino 2023-01-25 17:24:48 +00:00
parent d4f2dc85cc
commit 305067a6e6
2 changed files with 43 additions and 17 deletions

View File

@ -1075,6 +1075,18 @@ class PatchController(PatchService):
if not id_verification:
return dict(info=msg_info, warning=msg_warning, error=msg_error)
# Order patches such that
# If P2 requires P1
# P3 requires P2
# P4 requires P3
# Apply order: [P1, P2, P3, P4]
# Patch with lowest dependency gets applied first.
patch_list = self.patch_apply_remove_order(patch_list, reverse=True)
msg = "Patch Apply order: %s" % ",".join(patch_list)
LOG.info(msg)
audit_log_info(msg)
# Check for patches that can't be applied during an upgrade
upgrade_check = True
for patch_id in patch_list:
@ -1215,7 +1227,7 @@ class PatchController(PatchService):
return dict(info=msg_info, warning=msg_warning, error=msg_error)
def patch_remove_order(self, patch_ids):
def patch_apply_remove_order(self, patch_ids, reverse=False):
# Protect against duplications
patch_list = sorted(list(set(patch_ids)))
@ -1244,6 +1256,8 @@ class PatchController(PatchService):
patch_remove_order = dependency_list
patch_list = [patch_with_highest_dependency] + patch_remove_order
if reverse:
patch_list.reverse()
return patch_list
def patch_remove_api(self, patch_ids, **kwargs):
@ -1268,7 +1282,7 @@ class PatchController(PatchService):
if not id_verification:
return dict(info=msg_info, warning=msg_warning, error=msg_error)
patch_list = self.patch_remove_order(patch_ids)
patch_list = self.patch_apply_remove_order(patch_ids)
if patch_list is None:
msg = "Patch list provided belongs to different software versions."

View File

@ -299,20 +299,26 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
pc.patch_data.contents = copy.deepcopy(content_obj)
return metadata_obj["patch_id_list"]
def test_patch_remove_order_with_dependencies(self):
def test_patch_apply_remove_order_with_dependencies(self):
patch_ids = self.create_patch_data(self.pc, PATCH_LIST_WITH_DEPENDENCIES)
patch_list = self.pc.patch_remove_order(patch_ids)
patch_list = self.pc.patch_apply_remove_order(patch_ids)
self.assertEqual(patch_list,
["Fourth_Patch", "Third_Patch", "Second_Patch", "First_Patch"])
def test_patch_remove_order_different_sw_version(self):
def test_patch_apply_remove_order_reverse_with_dependencies(self):
patch_ids = self.create_patch_data(self.pc, PATCH_LIST_WITH_DEPENDENCIES)
patch_list = self.pc.patch_apply_remove_order(patch_ids, reverse=True)
self.assertEqual(patch_list,
["First_Patch", "Second_Patch", "Third_Patch", "Fourth_Patch"])
def test_patch_apply_remove_order_different_sw_version(self):
patch_ids = self.create_patch_data(self.pc, PATCH_LIST_WITH_DIFFERENT_SW_VERSION)
patch_list = self.pc.patch_remove_order(patch_ids)
patch_list = self.pc.patch_apply_remove_order(patch_ids)
self.assertIsNone(patch_list)
def test_patch_remove_order_single_patch(self):
def test_patch_apply_remove_order_single_patch(self):
patch_ids = self.create_patch_data(self.pc, SINGLE_PATCH)
patch_list = self.pc.patch_remove_order(patch_ids)
patch_list = self.pc.patch_apply_remove_order(patch_ids)
self.assertEqual(patch_list, ["First_Patch"])
def test_patch_remove_api_different_sw_versions(self):
@ -321,6 +327,12 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
self.assertEqual(response["error"],
"Patch list provided belongs to different software versions.\n")
def test_patch_remove_api_patch_not_in_metadata(self):
patch_ids = self.create_patch_data(self.pc, PATCH_NOT_IN_METADATA)
response = self.pc.patch_remove_api(patch_ids)
self.assertEqual(response["error"],
"Patch Second_Patch does not exist\n")
def test_patch_remove_api_patch_not_removable(self):
patch_ids = self.create_patch_data(self.pc, UNREMOVABLE_PATCH)
response = self.pc.patch_remove_api(patch_ids)
@ -488,9 +500,9 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
response = self.pc.patch_apply_api(patch_ids)
self.assertEqual(response["info"],
"First_Patch is already in the repo\n" +
"Fourth_Patch is already in the repo\n" +
"Second_Patch is already in the repo\n" +
"Third_Patch is already in the repo\n")
"Third_Patch is already in the repo\n" +
"Fourth_Patch is already in the repo\n")
def test_patch_apply_api_not_supported(self):
patch_ids = self.create_patch_data(self.pc,
@ -499,9 +511,9 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
response = self.pc.patch_apply_api(patch_ids)
self.assertEqual(response["info"],
"First_Patch is an unsupported patch format\n" +
"Fourth_Patch is an unsupported patch format\n" +
"Second_Patch is an unsupported patch format\n" +
"Third_Patch is an unsupported patch format\n")
"Third_Patch is an unsupported patch format\n" +
"Fourth_Patch is an unsupported patch format\n")
@mock.patch.object(LOG, 'exception')
@mock.patch.object(ostree_utils, 'get_feed_latest_commit')
@ -536,11 +548,11 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
self.assertEqual(response["info"],
"The base commit basecommit1 for First_Patch does not match " +
"the latest commit m on this system.\n" +
"The base commit commitThirdPatch2 for Fourth_Patch does not match " +
"the latest commit o on this system.\n" +
"The base commit commitFirstPatch for Second_Patch does not match " +
"the latest commit c on this system.\n" +
"the latest commit o on this system.\n" +
"The base commit commitSecondPatch for Third_Patch does not match " +
"the latest commit c on this system.\n" +
"The base commit commitThirdPatch2 for Fourth_Patch does not match " +
"the latest commit k on this system.\n")
@mock.patch.object(ostree_utils, 'get_feed_latest_commit')
@ -619,9 +631,9 @@ class CgcsPatchControllerTestCase(testtools.TestCase):
APPLY_PATCH_SUCCESSULLY,
CONTENTS_WITH_OSTREE_DATA)
_mock_feed.side_effect = ["basecommit1",
"commitThirdPatch2",
"commitFirstPatch",
"commitSecondPatch"]
"commitSecondPatch",
"commitThirdPatch2"]
self.pc.hosts = ["controller-0"]
self.pc.patch_apply_api(patch_ids)
self.assertEqual(self.pc.patch_data.metadata["First_Patch"]["repostate"], "Applied")