Skip to content

Commit 5776569

Browse files
committed
[AzL3Tdnf] Refactor: Addressing PR feedback #3.1
1 parent 262254b commit 5776569

File tree

5 files changed

+62
-64
lines changed

5 files changed

+62
-64
lines changed

src/core/src/package_managers/AzL3TdnfPackageManager.py

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,8 @@ class AzL3TdnfPackageManager(TdnfPackageManager):
2929
def __init__(self, env_layer, execution_config, composite_logger, telemetry_writer, status_handler):
3030
super(AzL3TdnfPackageManager, self).__init__(env_layer, execution_config, composite_logger, telemetry_writer, status_handler)
3131

32-
# Support to get updates and their dependencies
33-
self.tdnf_check = 'sudo tdnf -q list updates <SNAPSHOTTIME>'
34-
3532
# Install update
36-
self.install_security_updates_azgps_coordinated_cmd = 'sudo tdnf -y upgrade --skip-broken <SNAPSHOTTIME>'
33+
self.install_security_updates_azgps_coordinated_cmd = 'sudo tdnf -y upgrade --skip-broken'
3734

3835
# Strict SDP specializations
3936
self.TDNF_MINIMUM_VERSION_FOR_STRICT_SDP = "3.5.8-3.azl3" # minimum version of tdnf required to support Strict SDP in Azure Linux
@@ -51,14 +48,11 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
5148
execution_config.included_classifications_list = [Constants.PackageClassification.CRITICAL, Constants.PackageClassification.SECURITY, Constants.PackageClassification.OTHER]
5249

5350
# region Strict SDP using SnapshotTime
54-
@staticmethod
55-
def __generate_command_with_snapshotposixtime_if_specified(command_template, snapshot_posix_time=str()):
56-
# type: (str, str) -> str
57-
if snapshot_posix_time == str():
58-
return command_template.replace('<SNAPSHOTTIME>', str())
51+
def _Base__add_additional_parameters_as_required_to_cmd(self, cmd):
52+
if self.max_patch_publish_date == str():
53+
return cmd
5954
else:
60-
return command_template.replace('<SNAPSHOTTIME>', ('--snapshottime={0}'.format(str(snapshot_posix_time))))
61-
# endregion
55+
return cmd + ' --snapshottime={0}'.format(str(self.max_patch_publish_date))
6256

6357
# region Get Available Updates
6458
# region Classification-based (incl. All) update check
@@ -69,28 +63,15 @@ def get_all_updates(self, cached=False):
6963
self.composite_logger.log_debug("[AzL3TDNF] Get all updates : [Cached={0}][PackagesCount={1}]]".format(str(cached), len(self.all_updates_cached)))
7064
return self.all_updates_cached, self.all_update_versions_cached # allows for high performance reuse in areas of the code explicitly aware of the cache
7165

72-
out = self.invoke_package_manager(self.__generate_command_with_snapshotposixtime_if_specified(self.tdnf_check, self.max_patch_publish_date))
66+
out = self.invoke_package_manager(self._Base__add_additional_parameters_as_required_to_cmd(self.tdnf_check))
7367
self.all_updates_cached, self.all_update_versions_cached = self.extract_packages_and_versions(out)
7468
self.composite_logger.log_debug("[AzL3TDNF] Get all updates : [Cached={0}][PackagesCount={1}]]".format(str(False), len(self.all_updates_cached)))
7569
return self.all_updates_cached, self.all_update_versions_cached
7670

77-
def get_security_updates(self):
78-
"""Get missing security updates. NOTE: Classification based categorization of patches is not available in Azure Linux as of now"""
79-
self.composite_logger.log_verbose("[AzL3TDNF] Discovering all packages as 'security' packages, since TDNF does not support package classification...")
80-
security_packages, security_package_versions = self.get_all_updates(cached=False)
81-
self.composite_logger.log_debug("[AzL3TDNF] Discovered 'security' packages. [Count={0}]".format(len(security_packages)))
82-
return security_packages, security_package_versions
83-
84-
def get_other_updates(self):
85-
"""Get missing other updates."""
86-
self.composite_logger.log_verbose("[AzL3TDNF] Discovering 'other' packages...")
87-
return [], []
88-
8971
def set_max_patch_publish_date(self, max_patch_publish_date=str()):
9072
"""Set the max patch publish date in POSIX time for strict SDP"""
91-
self.composite_logger.log_debug("[AzL3TDNF] Setting max patch publish date. [MaxPatchPublishDate={0}]".format(str(max_patch_publish_date)))
9273
self.max_patch_publish_date = str(self.env_layer.datetime.datetime_string_to_posix_time(max_patch_publish_date, '%Y%m%dT%H%M%SZ')) if max_patch_publish_date != str() else max_patch_publish_date
93-
self.composite_logger.log_debug("[AzL3TDNF] Set max patch publish date. [MaxPatchPublishDatePosixTime={0}]".format(str(self.max_patch_publish_date)))
74+
self.composite_logger.log_debug("[AzL3TDNF] Set max patch publish date in posix time for Strict SDP. [MaxPatchPublishDate={0}][MaxPatchPublishDatePosixTime={1}]".format(str(max_patch_publish_date), str(self.max_patch_publish_date)))
9475
# endregion
9576

9677
# endregion
@@ -101,7 +82,7 @@ def install_updates_fail_safe(self, excluded_packages):
10182

10283
def install_security_updates_azgps_coordinated(self):
10384
"""Install security updates in Azure Linux following strict SDP"""
104-
command = self.__generate_command_with_snapshotposixtime_if_specified(self.install_security_updates_azgps_coordinated_cmd, self.max_patch_publish_date)
85+
command = self._Base__add_additional_parameters_as_required_to_cmd(self.install_security_updates_azgps_coordinated_cmd)
10586
out, code = self.invoke_package_manager_advanced(command, raise_on_exception=False)
10687
return code, out
10788

src/core/src/package_managers/PackageManager.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,8 @@ def install_security_updates_azgps_coordinated(self):
349349

350350
@abstractmethod
351351
def try_meet_azgps_coordinated_requirements(self):
352-
# type: () -> bool
353352
""" Returns true if the package manager meets the requirements for azgps coordinated security updates """
354-
return False
353+
pass
355354
# endregion
356355

357356
# region Package Information

src/core/src/package_managers/TdnfPackageManager.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import os
2020
import re
2121

22-
from abc import abstractmethod
22+
from abc import ABCMeta, abstractmethod
2323
from core.src.core_logic.VersionComparator import VersionComparator
2424
from core.src.bootstrap.Constants import Constants
2525
from core.src.package_managers.PackageManager import PackageManager
@@ -35,6 +35,7 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
3535
self.cmd_repo_refresh = "sudo tdnf -q list updates"
3636

3737
# Support to get updates and their dependencies
38+
self.tdnf_check = 'sudo tdnf -q list updates'
3839
self.single_package_check_versions = 'sudo tdnf list available <PACKAGE-NAME> '
3940
self.single_package_check_installed = 'sudo tdnf list installed <PACKAGE-NAME> '
4041
self.single_package_upgrade_simulation_cmd = 'sudo tdnf install --assumeno --skip-broken '
@@ -77,6 +78,8 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
7778

7879
self.package_install_expected_avg_time_in_seconds = 90 # Setting a default value of 90 seconds as the avg time to install a package using tdnf, might be changed later if needed.
7980

81+
__metaclass__ = ABCMeta # For Python 3.0+, it changes to class Abstract(metaclass=ABCMeta)
82+
8083
def refresh_repo(self):
8184
self.composite_logger.log("[TDNF] Refreshing local repo...")
8285
self.invoke_package_manager(self.cmd_clean_cache)
@@ -102,23 +105,36 @@ def invoke_package_manager_advanced(self, command, raise_on_exception=True):
102105
return out, code
103106

104107
# region Classification-based (incl. All) update check
105-
@abstractmethod
106108
def get_all_updates(self, cached=False):
107-
"""Same behavior as get_available_updates, but higher performance with no filters"""
108-
pass
109-
return [], [] # only here to suppress a static syntax validation problem
109+
"""Get all missing updates"""
110+
self.composite_logger.log_verbose("[TDNF] Discovering all packages...")
111+
if cached and not len(self.all_updates_cached) == 0:
112+
self.composite_logger.log_debug("[TDNF] Get all updates : [Cached={0}][PackagesCount={1}]]".format(str(cached), len(self.all_updates_cached)))
113+
return self.all_updates_cached, self.all_update_versions_cached # allows for high performance reuse in areas of the code explicitly aware of the cache
114+
115+
out = self.invoke_package_manager(self.__add_additional_parameters_as_required_to_cmd(self.tdnf_check))
116+
self.all_updates_cached, self.all_update_versions_cached = self.extract_packages_and_versions(out)
117+
self.composite_logger.log_debug("[TDNF] Get all updates : [Cached={0}][PackagesCount={1}]]".format(str(False), len(self.all_updates_cached)))
118+
return self.all_updates_cached, self.all_update_versions_cached
110119

111-
@abstractmethod
112120
def get_security_updates(self):
113-
pass
121+
"""Get missing security updates. NOTE: Classification based categorization of patches is not available in Azure Linux as of now"""
122+
self.composite_logger.log_verbose("[TDNF] Discovering all packages as 'security' packages, since TDNF does not support package classification...")
123+
security_packages, security_package_versions = self.get_all_updates(cached=False)
124+
self.composite_logger.log_debug("[TDNF] Discovered 'security' packages. [Count={0}]".format(len(security_packages)))
125+
return security_packages, security_package_versions
114126

115-
@abstractmethod
116127
def get_other_updates(self):
117-
pass
128+
"""Get missing other updates."""
129+
self.composite_logger.log_verbose("[TDNF] Discovering 'other' packages...")
130+
return [], []
118131

119-
@abstractmethod
120132
def set_max_patch_publish_date(self, max_patch_publish_date=str()):
121-
pass
133+
self.composite_logger.log_debug("[TDNF] Setting max patch publish date. [Date={0}]".format(str()))
134+
self.max_patch_publish_date = str()
135+
136+
def __add_additional_parameters_as_required_to_cmd(self, cmd):
137+
return cmd
122138
# endregion
123139

124140
# region Output Parser(s)
@@ -195,7 +211,6 @@ def install_updates_fail_safe(self, excluded_packages):
195211
def install_security_updates_azgps_coordinated(self):
196212
pass
197213

198-
@abstractmethod
199214
def try_meet_azgps_coordinated_requirements(self):
200215
# type: () -> bool
201216
""" Returns true if the package manager meets the requirements for azgps coordinated security updates """

src/core/tests/Test_AzL3TdnfPackageManager.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ def mock_linux_distribution_to_return_azure_linux(self):
4444
def mock_linux_distribution_to_return_azure_linux_2(self):
4545
return ['Common Base Linux Mariner', '2.0', '']
4646

47-
def mock_run_command_output_return_tdnf_3(self, cmd, no_output=False, chk_err=True):
48-
""" Mock for run_command_output to return tdnf 3 """
49-
return 0, "3.5.8-3\n"
50-
5147
def mock_run_command_output_return_1(self, cmd, no_output=False, chk_err=True):
5248
""" Mock for run_command_output to return None """
5349
return 1, "No output available\n"
@@ -119,24 +115,6 @@ def test_set_max_patch_publish_date(self):
119115
# posix time computation throws an exception if the date is not in the correct format
120116
self.assertRaises(ValueError, package_manager.set_max_patch_publish_date, "2024-07-02T00:00:00Z")
121117

122-
def test_get_tdnf_version(self):
123-
"""Unit test for tdnf package manager get_tdnf_version method"""
124-
package_manager = self.container.get('package_manager')
125-
self.assertTrue(package_manager is not None)
126-
self.backup_run_command_output = self.runtime.env_layer.run_command_output
127-
128-
test_input_output_table = [
129-
[self.mock_run_command_output_return_tdnf_3, "3.5.8-3"],
130-
[self.mock_run_command_output_return_1, None],
131-
]
132-
133-
for row in test_input_output_table:
134-
self.runtime.env_layer.run_command_output = row[0]
135-
version = package_manager.get_tdnf_version()
136-
self.assertEqual(version, row[1])
137-
138-
self.runtime.env_layer.run_command_output = self.backup_run_command_output
139-
140118
def test_is_mininum_tdnf_version_for_strict_sdp_installed(self):
141119
"""Unit test for tdnf package manager is_minimum_tdnf_version method"""
142120
package_manager = self.container.get('package_manager')

src/core/tests/Test_TdnfPackageManager.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from core.tests.library.RuntimeCompositor import RuntimeCompositor
2929

3030

31-
class TestAzL3TdnfPackageManager(unittest.TestCase):
31+
class TestTdnfPackageManager(unittest.TestCase):
3232
def setUp(self):
3333
self.runtime = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.TDNF)
3434
self.container = self.runtime.container
@@ -42,6 +42,14 @@ def mock_do_processes_require_restart_raise_exception(self):
4242

4343
def mock_write_with_retry_raise_exception(self, file_path_or_handle, data, mode='a+'):
4444
raise Exception
45+
46+
def mock_run_command_output_return_tdnf_3(self, cmd, no_output=False, chk_err=True):
47+
""" Mock for run_command_output to return tdnf 3 """
48+
return 0, "3.5.8-3\n"
49+
50+
def mock_run_command_output_return_1(self, cmd, no_output=False, chk_err=True):
51+
""" Mock for run_command_output to return None """
52+
return 1, "No output available\n"
4553
# endregion
4654

4755
# region Utility Functions
@@ -449,6 +457,23 @@ def test_revert_auto_os_update_to_system_default(self):
449457
self.__assert_std_io(captured_output=captured_output, expected_output=testcase["stdio"]["expected_output"])
450458
self.__assert_reverted_automatic_patch_configuration_settings(package_manager, config_exists=bool(testcase["assertions"]["config_exists"]), config_value_expected=testcase["assertions"]["config_value_expected"])
451459

460+
def test_get_tdnf_version(self):
461+
"""Unit test for tdnf package manager get_tdnf_version method"""
462+
package_manager = self.container.get('package_manager')
463+
self.assertTrue(package_manager is not None)
464+
self.backup_run_command_output = self.runtime.env_layer.run_command_output
465+
466+
test_input_output_table = [
467+
[self.mock_run_command_output_return_tdnf_3, "3.5.8-3"],
468+
[self.mock_run_command_output_return_1, None],
469+
]
470+
471+
for row in test_input_output_table:
472+
self.runtime.env_layer.run_command_output = row[0]
473+
version = package_manager.get_tdnf_version()
474+
self.assertEqual(version, row[1])
475+
476+
self.runtime.env_layer.run_command_output = self.backup_run_command_output
452477

453478
def test_package_manager_no_updates(self):
454479
"""Unit test for tdnf package manager with no updates"""

0 commit comments

Comments
 (0)