From dd3053de8e56a62a7a381a06b72af06ade72e9bd Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 21 Oct 2024 15:01:48 -0700 Subject: [PATCH 1/8] add retry method in core main for sudo check --- src/core/src/CoreMain.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/core/src/CoreMain.py b/src/core/src/CoreMain.py index a98160c7..5ca3a795 100644 --- a/src/core/src/CoreMain.py +++ b/src/core/src/CoreMain.py @@ -14,6 +14,7 @@ # # Requires Python 2.7+ import os +import time from core.src.bootstrap.Bootstrapper import Bootstrapper from core.src.bootstrap.Constants import Constants @@ -51,7 +52,12 @@ def __init__(self, argv): # Environment startup bootstrapper.bootstrap_splash_text() - bootstrapper.basic_environment_health_check() + + if status_handler.get_installation_reboot() == Constants.RebootStatus.COMPLETED: + self.__retry_sudo_check_after_reboot(6, 300, bootstrapper, composite_logger) + else: + bootstrapper.basic_environment_health_check() + lifecycle_manager.execution_start_check() # terminates if this instance shouldn't be running (redundant) # Execution config retrieval @@ -174,3 +180,21 @@ def is_temp_folder_available(env_layer, execution_config): and execution_config.temp_folder is not None \ and os.path.exists(execution_config.temp_folder) + def __retry_sudo_check_after_reboot(self, retries=6, time_interval=300, bootstrapper=None, composite_logger=None): + attempts = 0 + max_attempts = retries + + while attempts < max_attempts: + try: + bootstrapper.basic_environment_health_check() + break + except Exception as e: + attempts += 1 + composite_logger.log_debug("Attempt failed, error={0} ", e) + + if attempts < max_attempts: + composite_logger.log_debug("Retrying Interval={0} sec", time_interval) + time.sleep(time_interval) + else: + composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting ", max_attempts, attempts) + raise From cbc5e4bec0d15ae873685df35f4981bd381d3948 Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 21 Oct 2024 15:44:14 -0700 Subject: [PATCH 2/8] move rety logic to bootstrapper --- src/core/src/CoreMain.py | 23 ++--------------------- src/core/src/bootstrap/Bootstrapper.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/core/src/CoreMain.py b/src/core/src/CoreMain.py index 5ca3a795..2f64a604 100644 --- a/src/core/src/CoreMain.py +++ b/src/core/src/CoreMain.py @@ -53,8 +53,8 @@ def __init__(self, argv): # Environment startup bootstrapper.bootstrap_splash_text() - if status_handler.get_installation_reboot() == Constants.RebootStatus.COMPLETED: - self.__retry_sudo_check_after_reboot(6, 300, bootstrapper, composite_logger) + if status_handler is not None and status_handler.get_installation_reboot() == Constants.RebootStatus.COMPLETED: + bootstrapper.retry_check_sudo_status(retries=6, time_interval=300) else: bootstrapper.basic_environment_health_check() @@ -179,22 +179,3 @@ def is_temp_folder_available(env_layer, execution_config): and execution_config is not None \ and execution_config.temp_folder is not None \ and os.path.exists(execution_config.temp_folder) - - def __retry_sudo_check_after_reboot(self, retries=6, time_interval=300, bootstrapper=None, composite_logger=None): - attempts = 0 - max_attempts = retries - - while attempts < max_attempts: - try: - bootstrapper.basic_environment_health_check() - break - except Exception as e: - attempts += 1 - composite_logger.log_debug("Attempt failed, error={0} ", e) - - if attempts < max_attempts: - composite_logger.log_debug("Retrying Interval={0} sec", time_interval) - time.sleep(time_interval) - else: - composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting ", max_attempts, attempts) - raise diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 4e349f26..b217376d 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -19,6 +19,8 @@ import json import os import sys +import time + from core.src.bootstrap.ConfigurationFactory import ConfigurationFactory from core.src.bootstrap.Constants import Constants from core.src.bootstrap.Container import Container @@ -175,3 +177,22 @@ def check_sudo_status(self, raise_if_not_sudo=True): if raise_if_not_sudo: raise + def retry_check_sudo_status(self, retries=6, time_interval=300): + """ Retry check sudo status in os within half an hour at interval 300 sec(5 min) """ + attempts = 0 + max_attempts = retries + + while attempts < max_attempts: + try: + self.check_sudo_status() + break + except Exception as e: + attempts += 1 + self.composite_logger.log_debug("Attempt failed, error={0} ", e) + + if attempts < max_attempts: + self.composite_logger.log_debug("Retrying Interval={0} sec", time_interval) + time.sleep(time_interval) + else: + self.composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting ", max_attempts, attempts) + raise From 505f47d2d6934c68df708b3ae2590edb900780b7 Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 22 Oct 2024 10:50:12 -0700 Subject: [PATCH 3/8] add retry logic in bootstrapper class --- src/core/src/CoreMain.py | 8 +-- src/core/src/bootstrap/Bootstrapper.py | 73 +++++++++++++++----------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/core/src/CoreMain.py b/src/core/src/CoreMain.py index 2f64a604..f07f8bfd 100644 --- a/src/core/src/CoreMain.py +++ b/src/core/src/CoreMain.py @@ -14,7 +14,6 @@ # # Requires Python 2.7+ import os -import time from core.src.bootstrap.Bootstrapper import Bootstrapper from core.src.bootstrap.Constants import Constants @@ -52,12 +51,7 @@ def __init__(self, argv): # Environment startup bootstrapper.bootstrap_splash_text() - - if status_handler is not None and status_handler.get_installation_reboot() == Constants.RebootStatus.COMPLETED: - bootstrapper.retry_check_sudo_status(retries=6, time_interval=300) - else: - bootstrapper.basic_environment_health_check() - + bootstrapper.basic_environment_health_check() lifecycle_manager.execution_start_check() # terminates if this instance shouldn't be running (redundant) # Execution config retrieval diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index b217376d..afbf712a 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -144,39 +144,52 @@ def basic_environment_health_check(self): self.composite_logger.log("Process id: " + str(os.getpid())) # Ensure sudo works in the environment + # perform retry logic if it fails sudo_check_result = self.check_sudo_status() self.composite_logger.log_debug("Sudo status check: " + str(sudo_check_result) + "\n") def check_sudo_status(self, raise_if_not_sudo=True): """ Checks if we can invoke sudo successfully. """ - try: - self.composite_logger.log("Performing sudo status check... This should complete within 10 seconds.") - return_code, output = self.env_layer.run_command_output("timeout 10 sudo id && echo True || echo False", False, False) - # output should look like either this (bad): - # [sudo] password for username: - # False - # or this (good): - # uid=0(root) gid=0(root) groups=0(root) - # True - - output_lines = output.splitlines() - if len(output_lines) < 2: - raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) - - if output_lines[1] == "True": - return True - elif output_lines[1] == "False": - if raise_if_not_sudo: - raise Exception("Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) - return False - else: - raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) - except Exception as exception: - self.composite_logger.log_error("Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + - "Exception details: " + str(exception)) - if raise_if_not_sudo: - raise - + retry_interval = 300 + max_attemps = 6 + for attempt in range (max_attemps): + try: + self.composite_logger.log("Performing sudo status check... This should complete within 10 seconds.") + return_code, output = self.env_layer.run_command_output("timeout 10 sudo id && echo True || echo False", False, False) + # output should look like either this (bad): + # [sudo] password for username: + # False + # or this (good): + # uid=0(root) gid=0(root) groups=0(root) + # True + + output_lines = output.splitlines() + if len(output_lines) < 2: + raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + + if output_lines[1] == "True": + return True + elif output_lines[1] == "False": + if raise_if_not_sudo: + raise Exception("Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) + return False + else: + raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + except Exception as exception: + self.composite_logger.log_error("Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + + "Exception details: " + str(exception)) + if attempt == max_attemps - 1: + self.composite_logger.log_error("Maximum retry attempts reached.") + if raise_if_not_sudo: + raise + return False + + self.composite_logger.log_error("Retrying sudo status check in 5 minutes...") + time.sleep(retry_interval) + + # log when we do retry + # gather if retry happens, how many time it succeeded or failed + # key log msg - succeeded after try, fail after retry def retry_check_sudo_status(self, retries=6, time_interval=300): """ Retry check sudo status in os within half an hour at interval 300 sec(5 min) """ attempts = 0 @@ -188,11 +201,11 @@ def retry_check_sudo_status(self, retries=6, time_interval=300): break except Exception as e: attempts += 1 - self.composite_logger.log_debug("Attempt failed, error={0} ", e) + self.composite_logger.log_debug("Attempt failed, error={0}", e) if attempts < max_attempts: self.composite_logger.log_debug("Retrying Interval={0} sec", time_interval) time.sleep(time_interval) else: - self.composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting ", max_attempts, attempts) + self.composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting", max_attempts, attempts) raise From cf729256944a1ce09f27ddfb69284e9ca566a833 Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 22 Oct 2024 14:40:56 -0700 Subject: [PATCH 4/8] refactor retry logic in check_sudo_status --- src/core/src/bootstrap/Bootstrapper.py | 38 ++++++-------------------- src/core/src/bootstrap/Constants.py | 2 ++ 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index afbf712a..b23f34cf 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -150,9 +150,7 @@ def basic_environment_health_check(self): def check_sudo_status(self, raise_if_not_sudo=True): """ Checks if we can invoke sudo successfully. """ - retry_interval = 300 - max_attemps = 6 - for attempt in range (max_attemps): + for attempts in range(Constants.MAX_CHECK_SUDO_RETRY_COUNT): try: self.composite_logger.log("Performing sudo status check... This should complete within 10 seconds.") return_code, output = self.env_layer.run_command_output("timeout 10 sudo id && echo True || echo False", False, False) @@ -168,44 +166,24 @@ def check_sudo_status(self, raise_if_not_sudo=True): raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) if output_lines[1] == "True": + self.composite_logger.log("Sudo Check Successfully [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) return True elif output_lines[1] == "False": if raise_if_not_sudo: + self.composite_logger.log("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) raise Exception("Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) return False else: + self.composite_logger.log("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) except Exception as exception: self.composite_logger.log_error("Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + "Exception details: " + str(exception)) - if attempt == max_attemps - 1: - self.composite_logger.log_error("Maximum retry attempts reached.") + if attempts == Constants.MAX_CHECK_SUDO_RETRY_COUNT - 1: + self.composite_logger.log("Sudo Check Failed, reached [MaxAttempts={0}]".format(str(attempts))) if raise_if_not_sudo: raise return False - self.composite_logger.log_error("Retrying sudo status check in 5 minutes...") - time.sleep(retry_interval) - - # log when we do retry - # gather if retry happens, how many time it succeeded or failed - # key log msg - succeeded after try, fail after retry - def retry_check_sudo_status(self, retries=6, time_interval=300): - """ Retry check sudo status in os within half an hour at interval 300 sec(5 min) """ - attempts = 0 - max_attempts = retries - - while attempts < max_attempts: - try: - self.check_sudo_status() - break - except Exception as e: - attempts += 1 - self.composite_logger.log_debug("Attempt failed, error={0}", e) - - if attempts < max_attempts: - self.composite_logger.log_debug("Retrying Interval={0} sec", time_interval) - time.sleep(time_interval) - else: - self.composite_logger.log_debug("Max retries={0} reached after attempts={1}. Exiting", max_attempts, attempts) - raise + self.composite_logger.log_error("Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempts={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) diff --git a/src/core/src/bootstrap/Constants.py b/src/core/src/bootstrap/Constants.py index c016c027..591d6b86 100644 --- a/src/core/src/bootstrap/Constants.py +++ b/src/core/src/bootstrap/Constants.py @@ -220,6 +220,8 @@ class StatusTruncationConfig(EnumBackport): MAX_IMDS_CONNECTION_RETRY_COUNT = 5 MAX_ZYPPER_REPO_REFRESH_RETRY_COUNT = 5 MAX_COMPLETE_STATUS_FILES_TO_RETAIN = 10 + MAX_CHECK_SUDO_RETRY_COUNT = 6 + MAX_CHECK_SUDO_INTERVAL_IN_SEC = 300 class PackageBatchConfig(EnumBackport): # Batch Patching Parameters From 8d832da90e2efcef341d1688e5d6b676124eaf88 Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 22 Oct 2024 14:46:20 -0700 Subject: [PATCH 5/8] add eol in coremain --- src/core/src/CoreMain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/src/CoreMain.py b/src/core/src/CoreMain.py index f07f8bfd..a98160c7 100644 --- a/src/core/src/CoreMain.py +++ b/src/core/src/CoreMain.py @@ -173,3 +173,4 @@ def is_temp_folder_available(env_layer, execution_config): and execution_config is not None \ and execution_config.temp_folder is not None \ and os.path.exists(execution_config.temp_folder) + From d43a835225a6c4258f59524173149864f6ba1272 Mon Sep 17 00:00:00 2001 From: john feng Date: Sun, 27 Oct 2024 23:36:11 -0700 Subject: [PATCH 6/8] add ut for check sudo status failed --- src/core/src/bootstrap/Bootstrapper.py | 17 +++++++------- src/core/tests/Test_CoreMain.py | 18 ++++++++++++++ src/core/tests/library/RuntimeCompositor.py | 26 +++++++++++---------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index b23f34cf..64c3434d 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -19,7 +19,7 @@ import json import os import sys -import time +import threading from core.src.bootstrap.ConfigurationFactory import ConfigurationFactory from core.src.bootstrap.Constants import Constants @@ -150,7 +150,7 @@ def basic_environment_health_check(self): def check_sudo_status(self, raise_if_not_sudo=True): """ Checks if we can invoke sudo successfully. """ - for attempts in range(Constants.MAX_CHECK_SUDO_RETRY_COUNT): + for attempts in range(1, Constants.MAX_CHECK_SUDO_RETRY_COUNT + 1): try: self.composite_logger.log("Performing sudo status check... This should complete within 10 seconds.") return_code, output = self.env_layer.run_command_output("timeout 10 sudo id && echo True || echo False", False, False) @@ -163,27 +163,28 @@ def check_sudo_status(self, raise_if_not_sudo=True): output_lines = output.splitlines() if len(output_lines) < 2: + self.composite_logger.log_debug("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) if output_lines[1] == "True": - self.composite_logger.log("Sudo Check Successfully [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) + self.composite_logger.log_debug("Sudo Check Successfully [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) return True elif output_lines[1] == "False": if raise_if_not_sudo: - self.composite_logger.log("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) + self.composite_logger.log_debug("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) raise Exception("Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) return False else: - self.composite_logger.log("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) + self.composite_logger.log_debug("Sudo Check Failed [Attempts={0}][MaxAttempts={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) except Exception as exception: self.composite_logger.log_error("Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + "Exception details: " + str(exception)) - if attempts == Constants.MAX_CHECK_SUDO_RETRY_COUNT - 1: - self.composite_logger.log("Sudo Check Failed, reached [MaxAttempts={0}]".format(str(attempts))) + if attempts == Constants.MAX_CHECK_SUDO_RETRY_COUNT: + self.composite_logger.log_debug("Sudo Check Failed, reached [MaxAttempts={0}]".format(str(attempts))) if raise_if_not_sudo: raise return False self.composite_logger.log_error("Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempts={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) - time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) + threading.Event().wait(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) diff --git a/src/core/tests/Test_CoreMain.py b/src/core/tests/Test_CoreMain.py index 06d14965..dedc3b1c 100644 --- a/src/core/tests/Test_CoreMain.py +++ b/src/core/tests/Test_CoreMain.py @@ -1215,6 +1215,24 @@ def test_delete_temp_folder_contents_failure(self): os.remove = self.backup_os_remove runtime.stop() + def test_retry_check_sudo_status_attempts_failed(self): + # test retry logic in check sudo status all attempts failed + Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC = 1 + argument_composer = ArgumentComposer() + argument_composer.operation = Constants.ASSESSMENT + runtime = RuntimeCompositor(argument_composer.get_composed_arguments(), True, Constants.APT, set_mock_sudo_status=False) + CoreMain(argument_composer.get_composed_arguments()) + + with runtime.env_layer.file_system.open(runtime.execution_config.status_file_path, 'r') as file_handle: + substatus_file_data = json.load(file_handle)[0]["status"]["substatus"] + + self.assertTrue(substatus_file_data[0]["name"] == Constants.PATCH_ASSESSMENT_SUMMARY) + self.assertTrue(substatus_file_data[0]["status"].lower() == Constants.STATUS_ERROR.lower()) + self.assertTrue(Constants.PatchOperationErrorCodes.OPERATION_FAILED in substatus_file_data[0]["formattedMessage"]["message"]) + self.assertTrue('Unexpected sudo check result' in substatus_file_data[0]["formattedMessage"]["message"]) + + runtime.stop() + def __check_telemetry_events(self, runtime): all_events = os.listdir(runtime.telemetry_writer.events_folder_path) self.assertTrue(len(all_events) > 0) diff --git a/src/core/tests/library/RuntimeCompositor.py b/src/core/tests/library/RuntimeCompositor.py index d8f3e623..f97591a4 100644 --- a/src/core/tests/library/RuntimeCompositor.py +++ b/src/core/tests/library/RuntimeCompositor.py @@ -41,8 +41,9 @@ class RuntimeCompositor(object): - def __init__(self, argv=Constants.DEFAULT_UNSPECIFIED_VALUE, legacy_mode=False, package_manager_name=Constants.APT, vm_cloud_type=Constants.VMCloudType.AZURE): + def __init__(self, argv=Constants.DEFAULT_UNSPECIFIED_VALUE, legacy_mode=False, package_manager_name=Constants.APT, vm_cloud_type=Constants.VMCloudType.AZURE, set_mock_sudo_status=True): # Init data + self.set_mock_sudo_status = set_mock_sudo_status self.current_env = Constants.DEV os.environ[Constants.LPE_ENV_VARIABLE] = self.current_env self.argv = argv if argv != Constants.DEFAULT_UNSPECIFIED_VALUE else ArgumentComposer().get_composed_arguments() @@ -69,28 +70,29 @@ def mkdtemp_runner(): urlreq.urlopen = self.mock_urlopen # Adapted bootstrapper - bootstrapper = Bootstrapper(self.argv, capture_stdout=False) + self.bootstrapper = Bootstrapper(self.argv, capture_stdout=False) # Overriding sudo status check - Bootstrapper.check_sudo_status = self.check_sudo_status + if self.set_mock_sudo_status: + Bootstrapper.check_sudo_status = self.check_sudo_status # Reconfigure env layer for legacy mode tests - self.env_layer = bootstrapper.env_layer + self.env_layer = self.bootstrapper.env_layer if legacy_mode: self.legacy_env_layer_extensions = LegacyEnvLayerExtensions(package_manager_name) self.reconfigure_env_layer_to_legacy_mode() # Core components - self.container = bootstrapper.build_out_container() - self.file_logger = bootstrapper.file_logger - self.composite_logger = bootstrapper.composite_logger + self.container = self.bootstrapper.build_out_container() + self.file_logger = self.bootstrapper.file_logger + self.composite_logger = self.bootstrapper.composite_logger # re-initializing telemetry_writer, outside of Bootstrapper, to correctly set the env_layer configured for tests - self.telemetry_writer = TelemetryWriter(self.env_layer, self.composite_logger, bootstrapper.telemetry_writer.events_folder_path, bootstrapper.telemetry_supported) - bootstrapper.telemetry_writer = self.telemetry_writer - bootstrapper.composite_logger.telemetry_writer = self.telemetry_writer + self.telemetry_writer = TelemetryWriter(self.env_layer, self.composite_logger, self.bootstrapper.telemetry_writer.events_folder_path, self.bootstrapper.telemetry_supported) + self.bootstrapper.telemetry_writer = self.telemetry_writer + self.bootstrapper.composite_logger.telemetry_writer = self.telemetry_writer - self.lifecycle_manager, self.status_handler = bootstrapper.build_core_components(self.container) + self.lifecycle_manager, self.status_handler = self.bootstrapper.build_core_components(self.container) # Business logic components self.execution_config = self.container.get('execution_config') @@ -105,7 +107,7 @@ def mkdtemp_runner(): self.patch_assessor = self.container.get('patch_assessor') self.patch_installer = self.container.get('patch_installer') self.maintenance_window = self.container.get('maintenance_window') - self.vm_cloud_type = bootstrapper.configuration_factory.vm_cloud_type + self.vm_cloud_type = self.bootstrapper.configuration_factory.vm_cloud_type # Extension handler dependency self.write_ext_state_file(self.lifecycle_manager.ext_state_file_path, self.execution_config.sequence_number, datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ"), self.execution_config.operation) From 9fefc5c5c703e7610b836b16798289edf6515e00 Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 28 Oct 2024 11:02:23 -0700 Subject: [PATCH 7/8] add ut for check sudo status --- src/core/tests/Test_CoreMain.py | 20 ++++++++++++++++++++ src/core/tests/library/RuntimeCompositor.py | 11 +++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/core/tests/Test_CoreMain.py b/src/core/tests/Test_CoreMain.py index dedc3b1c..9ca5815c 100644 --- a/src/core/tests/Test_CoreMain.py +++ b/src/core/tests/Test_CoreMain.py @@ -1233,6 +1233,26 @@ def test_retry_check_sudo_status_attempts_failed(self): runtime.stop() + # def test_retry_check_sudo_status_attempts_success(self): + # # test retry logic in check sudo status all attempts failed + # Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC = 1 + # argument_composer = ArgumentComposer() + # argument_composer.operation = Constants.ASSESSMENT + # runtime = RuntimeCompositor(argument_composer.get_composed_arguments(), True, Constants.APT, set_mock_sudo_status=False) + # # result = runtime.mock_ev_layer_return() + # # print('result', result) + # CoreMain(argument_composer.get_composed_arguments()) + + # with runtime.env_layer.file_system.open(runtime.execution_config.status_file_path, 'r') as file_handle: + # substatus_file_data = json.load(file_handle)[0]["status"]["substatus"] + # + # self.assertTrue(substatus_file_data[0]["name"] == Constants.PATCH_ASSESSMENT_SUMMARY) + # self.assertTrue(substatus_file_data[0]["status"].lower() == Constants.STATUS_ERROR.lower()) + # self.assertTrue(Constants.PatchOperationErrorCodes.OPERATION_FAILED in substatus_file_data[0]["formattedMessage"]["message"]) + # self.assertTrue('Unexpected sudo check result' in substatus_file_data[0]["formattedMessage"]["message"]) + + # runtime.stop() + def __check_telemetry_events(self, runtime): all_events = os.listdir(runtime.telemetry_writer.events_folder_path) self.assertTrue(len(all_events) > 0) diff --git a/src/core/tests/library/RuntimeCompositor.py b/src/core/tests/library/RuntimeCompositor.py index f97591a4..86b34e61 100644 --- a/src/core/tests/library/RuntimeCompositor.py +++ b/src/core/tests/library/RuntimeCompositor.py @@ -72,12 +72,13 @@ def mkdtemp_runner(): # Adapted bootstrapper self.bootstrapper = Bootstrapper(self.argv, capture_stdout=False) + # Reconfigure env layer for legacy mode tests + self.env_layer = self.bootstrapper.env_layer + # Overriding sudo status check if self.set_mock_sudo_status: Bootstrapper.check_sudo_status = self.check_sudo_status - # Reconfigure env layer for legacy mode tests - self.env_layer = self.bootstrapper.env_layer if legacy_mode: self.legacy_env_layer_extensions = LegacyEnvLayerExtensions(package_manager_name) self.reconfigure_env_layer_to_legacy_mode() @@ -167,6 +168,12 @@ def mock_sleep(self, seconds): def check_sudo_status(self, raise_if_not_sudo=True): return True + # def mock_ev_layer_return(self): + # self.env_layer.run_command_output = self.mock_layer + + def mock_layer(self): + return 0, 'True' + def get_current_auto_os_patch_state(self): return Constants.AutomaticOSPatchStates.DISABLED From 415f307503fb6f5f590dc9f2fce067be0b0bb64a Mon Sep 17 00:00:00 2001 From: john feng Date: Wed, 6 Nov 2024 13:41:48 -0800 Subject: [PATCH 8/8] refactor sudo check test --- src/core/src/bootstrap/Bootstrapper.py | 5 +++-- src/core/tests/Test_CoreMain.py | 22 +-------------------- src/core/tests/library/RuntimeCompositor.py | 9 +++++---- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 64c3434d..1a0b1272 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -19,7 +19,7 @@ import json import os import sys -import threading +import time from core.src.bootstrap.ConfigurationFactory import ConfigurationFactory from core.src.bootstrap.Constants import Constants @@ -154,6 +154,7 @@ def check_sudo_status(self, raise_if_not_sudo=True): try: self.composite_logger.log("Performing sudo status check... This should complete within 10 seconds.") return_code, output = self.env_layer.run_command_output("timeout 10 sudo id && echo True || echo False", False, False) + print('what is return_code', return_code, 'whats output', output) # output should look like either this (bad): # [sudo] password for username: # False @@ -187,4 +188,4 @@ def check_sudo_status(self, raise_if_not_sudo=True): return False self.composite_logger.log_error("Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempts={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) - threading.Event().wait(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) + time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) diff --git a/src/core/tests/Test_CoreMain.py b/src/core/tests/Test_CoreMain.py index 9ca5815c..cca08cd1 100644 --- a/src/core/tests/Test_CoreMain.py +++ b/src/core/tests/Test_CoreMain.py @@ -1217,7 +1217,7 @@ def test_delete_temp_folder_contents_failure(self): def test_retry_check_sudo_status_attempts_failed(self): # test retry logic in check sudo status all attempts failed - Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC = 1 + # Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC = 1 argument_composer = ArgumentComposer() argument_composer.operation = Constants.ASSESSMENT runtime = RuntimeCompositor(argument_composer.get_composed_arguments(), True, Constants.APT, set_mock_sudo_status=False) @@ -1233,26 +1233,6 @@ def test_retry_check_sudo_status_attempts_failed(self): runtime.stop() - # def test_retry_check_sudo_status_attempts_success(self): - # # test retry logic in check sudo status all attempts failed - # Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC = 1 - # argument_composer = ArgumentComposer() - # argument_composer.operation = Constants.ASSESSMENT - # runtime = RuntimeCompositor(argument_composer.get_composed_arguments(), True, Constants.APT, set_mock_sudo_status=False) - # # result = runtime.mock_ev_layer_return() - # # print('result', result) - # CoreMain(argument_composer.get_composed_arguments()) - - # with runtime.env_layer.file_system.open(runtime.execution_config.status_file_path, 'r') as file_handle: - # substatus_file_data = json.load(file_handle)[0]["status"]["substatus"] - # - # self.assertTrue(substatus_file_data[0]["name"] == Constants.PATCH_ASSESSMENT_SUMMARY) - # self.assertTrue(substatus_file_data[0]["status"].lower() == Constants.STATUS_ERROR.lower()) - # self.assertTrue(Constants.PatchOperationErrorCodes.OPERATION_FAILED in substatus_file_data[0]["formattedMessage"]["message"]) - # self.assertTrue('Unexpected sudo check result' in substatus_file_data[0]["formattedMessage"]["message"]) - - # runtime.stop() - def __check_telemetry_events(self, runtime): all_events = os.listdir(runtime.telemetry_writer.events_folder_path) self.assertTrue(len(all_events) > 0) diff --git a/src/core/tests/library/RuntimeCompositor.py b/src/core/tests/library/RuntimeCompositor.py index 86b34e61..a88babf6 100644 --- a/src/core/tests/library/RuntimeCompositor.py +++ b/src/core/tests/library/RuntimeCompositor.py @@ -79,6 +79,10 @@ def mkdtemp_runner(): if self.set_mock_sudo_status: Bootstrapper.check_sudo_status = self.check_sudo_status + else: + self.env_layer.run_command_output = self.mock_run_command + print('did run_command get called') + if legacy_mode: self.legacy_env_layer_extensions = LegacyEnvLayerExtensions(package_manager_name) self.reconfigure_env_layer_to_legacy_mode() @@ -168,10 +172,7 @@ def mock_sleep(self, seconds): def check_sudo_status(self, raise_if_not_sudo=True): return True - # def mock_ev_layer_return(self): - # self.env_layer.run_command_output = self.mock_layer - - def mock_layer(self): + def mock_run_command(self): return 0, 'True' def get_current_auto_os_patch_state(self):