From 04859faf6c9bff0f2130a864e7f98e14b93afece Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 21 Apr 2025 22:34:18 -0700 Subject: [PATCH 01/10] add [BST] prefix to logs, fix the bug condition checking attempts >=1 --- src/core/src/bootstrap/Bootstrapper.py | 27 +++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 50d66b03..8c18c712 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -144,22 +144,24 @@ def basic_environment_health_check(self): # Ensure sudo works in the environment sudo_check_result = self.check_sudo_status_with_retry() - self.composite_logger.log_debug("Sudo status check: " + str(sudo_check_result) + "\n") + self.composite_logger.log_debug("[BST] Sudo status check: " + str(sudo_check_result) + "\n") def check_sudo_status_with_retry(self, raise_if_not_sudo=True): # type:(bool) -> any - """ retry to invoke sudo check """ + """ Retry up to max six times to invoke sudo check """ + + self.composite_logger.log("[BST] Performing sudo status check... This should complete within 10 seconds.") for attempts in range(1, Constants.MAX_CHECK_SUDO_RETRY_COUNT + 1): try: sudo_status = self.check_sudo_status(raise_if_not_sudo=raise_if_not_sudo) - if sudo_status and attempts > 1: - self.composite_logger.log_debug("Sudo Check Successfully [RetryCount={0}][MaxRetryCount={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) + if sudo_status and attempts >= 1: + self.composite_logger.log_debug("[BST] Sudo Check Successfully [RetryCount={0}][MaxRetryCount={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) return sudo_status elif sudo_status is None or sudo_status is False: if attempts < Constants.MAX_CHECK_SUDO_RETRY_COUNT: - self.composite_logger.log_debug("Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) continue @@ -168,18 +170,17 @@ def check_sudo_status_with_retry(self, raise_if_not_sudo=True): except Exception as exception: if attempts >= Constants.MAX_CHECK_SUDO_RETRY_COUNT: - self.composite_logger.log_error("Customer environment error (sudo failure). [Exception={0}][MaxRetryCount={1}]".format(str(exception), str(attempts))) + self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][MaxRetryCount={1}]".format(str(exception), str(attempts))) if raise_if_not_sudo: raise - self.composite_logger.log_debug("Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) def check_sudo_status(self, raise_if_not_sudo=True): # type:(bool) -> any """ 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) + return_code, output = self.env_layer.run_command_output("[BST] timeout 10 sudo id && echo True || echo False", False, False) # output should look like either this (bad): # [sudo] password for username: # False @@ -189,18 +190,18 @@ def check_sudo_status(self, raise_if_not_sudo=True): output_lines = output.splitlines() if len(output_lines) < 2: - raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + raise Exception("[BST] 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"))) + raise Exception("[BST] Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) return False else: - raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + raise Exception("[BST] Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) except Exception as exception: - self.composite_logger.log_debug("Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + + self.composite_logger.log_debug("[BST] Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + "Exception details: " + str(exception)) if raise_if_not_sudo: raise From 60fdb319e80e23705af3b48c734ad8052a5ff149 Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 21 Apr 2025 22:53:20 -0700 Subject: [PATCH 02/10] add ut handling build_out_container raise exception --- src/core/tests/Test_Bootstrapper.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/core/tests/Test_Bootstrapper.py b/src/core/tests/Test_Bootstrapper.py index 9df4859b..df463a95 100644 --- a/src/core/tests/Test_Bootstrapper.py +++ b/src/core/tests/Test_Bootstrapper.py @@ -61,6 +61,9 @@ def mock_retry_run_command_output(self, command, no_output=False, chk_err=True): # Mock success (True) on the 3rd attempt elif self.sudo_check_status_attempts == 3: return (0, "uid=0(root) gid=0(root) groups=0(root)\nTrue") + + def mock_get_arguments_configuration(self, argv): + raise Exception("EXCEPTION during patch management core bootstrap:") # end regions mock def test_check_sudo_status_all_attempts_failed(self): @@ -113,7 +116,23 @@ def test_check_sudo_status_succeeds_on_third_attempt(self): # Verify 3 attempts were made self.assertEqual(self.sudo_check_status_attempts, 3, "Expected exactly 3 attempts in check_sudo_status") - + + def test_build_out_container_throw_exception(self): + # Test build_out_container throws exception when no container name is provided + + # Save original methods + original_get_arguments_configuration = self.runtime.bootstrapper.configuration_factory.get_arguments_configuration + + # Mock + self.runtime.bootstrapper.configuration_factory.get_arguments_configuration = self.mock_get_arguments_configuration + + # Verify exception + with self.assertRaises(Exception) as context: + self.runtime.bootstrapper.build_out_container() + + # Restore original methods + self.runtime.bootstrapper.configuration_factory.get_arguments_configuration = original_get_arguments_configuration + if __name__ == '__main__': unittest.main() From 57c5f52657305c3683910bd043b979a7891cc342 Mon Sep 17 00:00:00 2001 From: john feng Date: Mon, 21 Apr 2025 23:39:07 -0700 Subject: [PATCH 03/10] add ut for reset_auto_assessment_log_file_if_needed_raise_exception --- src/core/tests/Test_Bootstrapper.py | 54 ++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/core/tests/Test_Bootstrapper.py b/src/core/tests/Test_Bootstrapper.py index df463a95..244450d4 100644 --- a/src/core/tests/Test_Bootstrapper.py +++ b/src/core/tests/Test_Bootstrapper.py @@ -13,7 +13,14 @@ # limitations under the License. # # Requires Python 2.7+ +import os +import sys import unittest +# Conditional import for StringIO +try: + from StringIO import StringIO # Python 2 +except ImportError: + from io import StringIO # Python 3 from core.src.bootstrap.Constants import Constants from core.tests.library.ArgumentComposer import ArgumentComposer @@ -21,6 +28,9 @@ class TestBootstrapper(unittest.TestCase): + def __init__(self, methodName: str = "runTest"): + super().__init__(methodName) + def setUp(self): self.sudo_check_status_attempts = 0 Constants.SET_CHECK_SUDO_STATUS_TRUE = False @@ -64,6 +74,15 @@ def mock_retry_run_command_output(self, command, no_output=False, chk_err=True): def mock_get_arguments_configuration(self, argv): raise Exception("EXCEPTION during patch management core bootstrap:") + + def mock_os_path_exists(self, path): + return True + + def mock_os_path_getsize(self, path): + return Constants.MAX_AUTO_ASSESSMENT_LOGFILE_SIZE_IN_BYTES + 1 + + def mock_os_remove(self, path): + raise Exception("Mocked exception in os.remove") # end regions mock def test_check_sudo_status_all_attempts_failed(self): @@ -132,7 +151,40 @@ def test_build_out_container_throw_exception(self): # Restore original methods self.runtime.bootstrapper.configuration_factory.get_arguments_configuration = original_get_arguments_configuration + self.runtime.stop() + + def test_reset_auto_assessment_log_file_if_needed_raise_exception(self): + # Arrange, Capture stdout + captured_output = StringIO() + original_output = sys.stdout + sys.stdout = captured_output # Redirect stdout to the StringIO object + + # Save original methods + self.runtime.bootstrapper.auto_assessment_only = True + original_path_exists = os.path.exists + original_path_getsize = os.path.getsize + original_os_remove = os.remove + + # Mock + os.path.exists = self.mock_os_path_exists + os.path.getsize = self.mock_os_path_getsize + os.remove = self.mock_os_remove + + self.runtime.bootstrapper.reset_auto_assessment_log_file_if_needed() + + # Restore stdout + sys.stdout = original_output + + # Assert + output = captured_output.getvalue() + self.assertIn("INFO: Error while checking/removing auto-assessment log file.", output) # Verify the log output contains the expected text + + # Restore original methods + os.path.exists = original_path_exists + os.path.getsize = original_path_getsize + os.remove = original_os_remove + self.runtime.stop() + - if __name__ == '__main__': unittest.main() From 918f0ea9047a1fe75b9ea9198a0d0762b7c9df0d Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 22 Apr 2025 23:01:25 -0700 Subject: [PATCH 04/10] rename retry to attempt --- src/core/src/bootstrap/Bootstrapper.py | 26 +++++++++++++------------- src/core/src/bootstrap/Constants.py | 2 +- src/core/tests/Test_Bootstrapper.py | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 8c18c712..fbda8bfa 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -107,7 +107,7 @@ def get_value_from_argv(argv, key, default_value=Constants.DEFAULT_UNSPECIFIED_V return str(argv[x+1]) if default_value == Constants.DEFAULT_UNSPECIFIED_VALUE: - raise Exception("Unable to find key {0} in core arguments: {1}.".format(key, str(argv))) + raise Exception("[BST] Unable to find key {0} in core arguments: {1}.".format(key, str(argv))) else: return default_value @@ -123,7 +123,7 @@ def build_out_container(self): return self.container except Exception as error: - self.composite_logger.log_error('\nEXCEPTION during patch management core bootstrap: ' + repr(error)) + self.composite_logger.log_error('\n[BST] EXCEPTION during patch management core bootstrap: ' + repr(error)) raise pass @@ -143,37 +143,37 @@ def basic_environment_health_check(self): self.composite_logger.log("Process id: " + str(os.getpid())) # Ensure sudo works in the environment - sudo_check_result = self.check_sudo_status_with_retry() + sudo_check_result = self.check_sudo_status_with_attempts() self.composite_logger.log_debug("[BST] Sudo status check: " + str(sudo_check_result) + "\n") - def check_sudo_status_with_retry(self, raise_if_not_sudo=True): + def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): # type:(bool) -> any - """ Retry up to max six times to invoke sudo check """ + """ Attempt(s) up to max six times to invoke sudo check """ self.composite_logger.log("[BST] Performing sudo status check... This should complete within 10 seconds.") - for attempts in range(1, Constants.MAX_CHECK_SUDO_RETRY_COUNT + 1): + for attempts in range(1, Constants.MAX_CHECK_SUDO_ATTEMPTS + 1): try: sudo_status = self.check_sudo_status(raise_if_not_sudo=raise_if_not_sudo) if sudo_status and attempts >= 1: - self.composite_logger.log_debug("[BST] Sudo Check Successfully [RetryCount={0}][MaxRetryCount={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_RETRY_COUNT)) + self.composite_logger.log_debug("[BST] Sudo Check Successfully [Attempt(s)={0}][MaxAttempt(s)={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) return sudo_status elif sudo_status is None or sudo_status is False: - if attempts < Constants.MAX_CHECK_SUDO_RETRY_COUNT: - self.composite_logger.log_debug("[BST] Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + if attempts < Constants.MAX_CHECK_SUDO_ATTEMPTS: + self.composite_logger.log_debug("[BST] Attempt sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) continue - elif attempts >= Constants.MAX_CHECK_SUDO_RETRY_COUNT: + elif attempts >= Constants.MAX_CHECK_SUDO_ATTEMPTS: raise except Exception as exception: - if attempts >= Constants.MAX_CHECK_SUDO_RETRY_COUNT: - self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][MaxRetryCount={1}]".format(str(exception), str(attempts))) + if attempts >= Constants.MAX_CHECK_SUDO_ATTEMPTS: + self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][Attempt(s)={1}[MaxAttempt(s)={2}]".format(str(exception), str(attempts), str(attempts))) if raise_if_not_sudo: raise - self.composite_logger.log_debug("[BST] Retrying sudo status check after a delay of [ElapsedTimeInSeconds={0}][RetryCount={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Attempt sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) def check_sudo_status(self, raise_if_not_sudo=True): diff --git a/src/core/src/bootstrap/Constants.py b/src/core/src/bootstrap/Constants.py index f5aa2af9..4745bada 100644 --- a/src/core/src/bootstrap/Constants.py +++ b/src/core/src/bootstrap/Constants.py @@ -224,7 +224,7 @@ class StatusTruncationConfig(EnumBackport): MAX_ZYPPER_REPO_REFRESH_RETRY_COUNT = 5 MAX_COMPLETE_STATUS_FILES_TO_RETAIN = 10 SET_CHECK_SUDO_STATUS_TRUE = True - MAX_CHECK_SUDO_RETRY_COUNT = 6 + MAX_CHECK_SUDO_ATTEMPTS = 6 MAX_CHECK_SUDO_INTERVAL_IN_SEC = 300 class PackageBatchConfig(EnumBackport): diff --git a/src/core/tests/Test_Bootstrapper.py b/src/core/tests/Test_Bootstrapper.py index 244450d4..bdb1c78e 100644 --- a/src/core/tests/Test_Bootstrapper.py +++ b/src/core/tests/Test_Bootstrapper.py @@ -33,7 +33,7 @@ def __init__(self, methodName: str = "runTest"): def setUp(self): self.sudo_check_status_attempts = 0 - Constants.SET_CHECK_SUDO_STATUS_TRUE = False + Constants.SET_CHECK_SUDO_STATUS_TRUE = False # override check_sudo_status in RuntimeCompositor argument_composer = ArgumentComposer() argument_composer.operation = Constants.ASSESSMENT self.argv = argument_composer.get_composed_arguments() @@ -89,7 +89,7 @@ def test_check_sudo_status_all_attempts_failed(self): # Set raise_if_not_sudo=False to test the `return False` all attempts failed self.runtime.env_layer.run_command_output = self.mock_false_run_command_output - result = self.runtime.bootstrapper.check_sudo_status_with_retry(raise_if_not_sudo=False) + result = self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=False) # Verify check_sudo_status_with_retry is False self.assertEqual(result, None, "Expected check_sudo_status retry to return None after all attempts failed") @@ -98,7 +98,7 @@ def test_check_sudo_status_throw_exception(self): # Set raise_if_not_sudo=True to throw exception) after all retries self.runtime.env_layer.run_command_output = self.mock_false_run_command_output with self.assertRaises(Exception) as context: - self.runtime.bootstrapper.check_sudo_status_with_retry(raise_if_not_sudo=True) + self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=True) # Verify exception msg contains the expected failure text self.assertTrue("Unable to invoke sudo successfully" in str(context.exception)) @@ -108,7 +108,7 @@ def test_check_sudo_status_insufficient_output_lines(self): self.runtime.env_layer.run_command_output = self.mock_insufficient_run_command_output with self.assertRaises(Exception) as context: - self.runtime.bootstrapper.check_sudo_status_with_retry() + self.runtime.bootstrapper.check_sudo_status_with_attempts() # Verify exception msg contains the expected failure text self.assertTrue("Unexpected sudo check result" in str(context.exception)) @@ -118,7 +118,7 @@ def test_check_sudo_status_unexpected_output_lines(self): self.runtime.env_layer.run_command_output = self.mock_unexpected_output_run_command_output with self.assertRaises(Exception) as context: - self.runtime.bootstrapper.check_sudo_status_with_retry() + self.runtime.bootstrapper.check_sudo_status_with_attempts() # Verify exception msg contains the expected failure text self.assertTrue("Unexpected sudo check result" in str(context.exception)) @@ -128,7 +128,7 @@ def test_check_sudo_status_succeeds_on_third_attempt(self): self.runtime.env_layer.run_command_output = self.mock_retry_run_command_output # Attempt to check sudo status, succeed (true) on the 3rd attempt - result = self.runtime.bootstrapper.check_sudo_status_with_retry(raise_if_not_sudo=True) + result = self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=True) # Verify the result is success (True) self.assertTrue(result, "Expected check_sudo_status to succeed on the 3rd attempts") From d68a2b9d0b52d4f1fe9007386568ef464e651329 Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 22 Apr 2025 23:18:42 -0700 Subject: [PATCH 05/10] reword retry to attempt --- src/core/tests/Test_Bootstrapper.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/tests/Test_Bootstrapper.py b/src/core/tests/Test_Bootstrapper.py index bdb1c78e..7ccd14c4 100644 --- a/src/core/tests/Test_Bootstrapper.py +++ b/src/core/tests/Test_Bootstrapper.py @@ -46,29 +46,29 @@ def tearDown(self): # regions mock def mock_false_run_command_output(self, command, no_output=False, chk_err=True): - """Mock a failed sudo check status command output to test retry logic.""" - # Mock failure to trigger retry logic in check_sudo_status + """Mock a failed sudo check status command output to test multiple attempts logic.""" + # Mock failure to trigger multiple attempts logic in check_sudo_status return (1, "[sudo] password for user:\nFalse") def mock_insufficient_run_command_output(self, command, no_output=False, chk_err=True): """Mock an insufficient output line in sudo check status command output.""" - # Mock failure to trigger retry logic in check_sudo_status + # Mock failure to trigger multiple attempts logic in check_sudo_status return (1, "[sudo] password for user:") def mock_unexpected_output_run_command_output(self, command, no_output=False, chk_err=True): """Mock an unexpected output line in sudo check status command output.""" - # Mock failure to trigger retry logic in check_sudo_status + # Mock failure to trigger multiple attempts logic in check_sudo_status return (1, "[sudo] password for user:\nUnexpectedOutput") - def mock_retry_run_command_output(self, command, no_output=False, chk_err=True): - """Mock 3 failed sudo check status attempts followed by a success on the 4th attempt.""" + def mock_run_command_output_with_attempts(self, command, no_output=False, chk_err=True): + """Mock 3 failed sudo check status attempts followed by a success on the 4th attempts.""" self.sudo_check_status_attempts += 1 # Mock failure on the first two attempts if self.sudo_check_status_attempts <= 2: return (1, "[sudo] password for user:\nFalse") - # Mock success (True) on the 3rd attempt + # Mock success (True) on the 3rd attempts elif self.sudo_check_status_attempts == 3: return (0, "uid=0(root) gid=0(root) groups=0(root)\nTrue") @@ -91,8 +91,8 @@ def test_check_sudo_status_all_attempts_failed(self): result = self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=False) - # Verify check_sudo_status_with_retry is False - self.assertEqual(result, None, "Expected check_sudo_status retry to return None after all attempts failed") + # Verify check_sudo_status_with_attempts is False + self.assertEqual(result, None, "Expected check_sudo_status_with_attempts to return None after all attempts failed") def test_check_sudo_status_throw_exception(self): # Set raise_if_not_sudo=True to throw exception) after all retries @@ -123,11 +123,11 @@ def test_check_sudo_status_unexpected_output_lines(self): # Verify exception msg contains the expected failure text self.assertTrue("Unexpected sudo check result" in str(context.exception)) - def test_check_sudo_status_succeeds_on_third_attempt(self): - # Test retry logic in check sudo status after 2 failed attempts followed by success (true) - self.runtime.env_layer.run_command_output = self.mock_retry_run_command_output + def test_check_sudo_status_succeeds_on_third_attempts(self): + # Test check sudo status after 2 failed attempts followed by success (true) + self.runtime.env_layer.run_command_output = self.mock_run_command_output_with_attempts - # Attempt to check sudo status, succeed (true) on the 3rd attempt + # Attempt to check sudo status, succeed (true) on the 3rd attempts result = self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=True) # Verify the result is success (True) From 6924c18dddb4e41077d201577d9cfd504c0771ae Mon Sep 17 00:00:00 2001 From: john feng Date: Wed, 23 Apr 2025 12:30:31 -0700 Subject: [PATCH 06/10] correct sudo check error msg --- src/core/src/bootstrap/Bootstrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index fbda8bfa..5cdddd60 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -170,7 +170,7 @@ def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): except Exception as exception: if attempts >= Constants.MAX_CHECK_SUDO_ATTEMPTS: - self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][Attempt(s)={1}[MaxAttempt(s)={2}]".format(str(exception), str(attempts), str(attempts))) + self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][Attempt(s)={1}][MaxAttempt(s)={2}]".format(str(exception), str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) if raise_if_not_sudo: raise self.composite_logger.log_debug("[BST] Attempt sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) From 87144da33e61f8d051f6dc0a726b15e7458d7170 Mon Sep 17 00:00:00 2001 From: john feng Date: Tue, 29 Apr 2025 09:12:43 -0700 Subject: [PATCH 07/10] remove bst prefix in run_command_output --- src/core/src/bootstrap/Bootstrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 5cdddd60..d9669c45 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -180,7 +180,7 @@ def check_sudo_status(self, raise_if_not_sudo=True): # type:(bool) -> any """ Checks if we can invoke sudo successfully. """ try: - return_code, output = self.env_layer.run_command_output("[BST] timeout 10 sudo id && echo True || echo False", False, False) + 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 From 8a367b8f12247eb16eb2cefca7ac5c352ec5b787 Mon Sep 17 00:00:00 2001 From: john feng Date: Wed, 30 Apr 2025 16:32:27 -0700 Subject: [PATCH 08/10] remove [bst] from exception --- src/core/src/bootstrap/Bootstrapper.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index d9669c45..4522475b 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -107,7 +107,7 @@ def get_value_from_argv(argv, key, default_value=Constants.DEFAULT_UNSPECIFIED_V return str(argv[x+1]) if default_value == Constants.DEFAULT_UNSPECIFIED_VALUE: - raise Exception("[BST] Unable to find key {0} in core arguments: {1}.".format(key, str(argv))) + raise Exception("Unable to find key {0} in core arguments: {1}.".format(key, str(argv))) else: return default_value @@ -123,7 +123,7 @@ def build_out_container(self): return self.container except Exception as error: - self.composite_logger.log_error('\n[BST] EXCEPTION during patch management core bootstrap: ' + repr(error)) + self.composite_logger.log_error('\nEXCEPTION during patch management core bootstrap: ' + repr(error)) raise pass @@ -190,16 +190,16 @@ def check_sudo_status(self, raise_if_not_sudo=True): output_lines = output.splitlines() if len(output_lines) < 2: - raise Exception("[BST] Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + 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("[BST] Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) + raise Exception("Unable to invoke sudo successfully. Output: " + " ".join(output.split("\n"))) return False else: - raise Exception("[BST] Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) + raise Exception("Unexpected sudo check result. Output: " + " ".join(output.split("\n"))) except Exception as exception: self.composite_logger.log_debug("[BST] Sudo status check failed. Please ensure the computer is configured correctly for sudo invocation. " + "Exception details: " + str(exception)) From 6261c506d02d56fa7c9744e140904adf0302e820 Mon Sep 17 00:00:00 2001 From: john feng Date: Wed, 30 Apr 2025 18:17:39 -0700 Subject: [PATCH 09/10] refactor log msgs --- src/core/src/bootstrap/Bootstrapper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index 4522475b..14986e2e 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -156,12 +156,12 @@ def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): sudo_status = self.check_sudo_status(raise_if_not_sudo=raise_if_not_sudo) if sudo_status and attempts >= 1: - self.composite_logger.log_debug("[BST] Sudo Check Successfully [Attempt(s)={0}][MaxAttempt(s)={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) + self.composite_logger.log_debug("[BST] Sudo status check completed successfully.[Attempt(s)={0}][MaxAttempt(s)={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) return sudo_status elif sudo_status is None or sudo_status is False: if attempts < Constants.MAX_CHECK_SUDO_ATTEMPTS: - self.composite_logger.log_debug("[BST] Attempt sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay.[ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) continue @@ -170,10 +170,10 @@ def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): except Exception as exception: if attempts >= Constants.MAX_CHECK_SUDO_ATTEMPTS: - self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Exception={0}][Attempt(s)={1}][MaxAttempt(s)={2}]".format(str(exception), str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) + self.composite_logger.log_error("[BST] Customer environment error (sudo failure).[Exception={0}][Attempt(s)={1}][MaxAttempt(s)={2}]".format(str(exception), str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) if raise_if_not_sudo: raise - self.composite_logger.log_debug("[BST] Attempt sudo status check after a delay of [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay.[ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) def check_sudo_status(self, raise_if_not_sudo=True): From be0add0f44dcb373e042a1f82de38d7efd96234b Mon Sep 17 00:00:00 2001 From: john feng Date: Wed, 7 May 2025 08:56:18 -0700 Subject: [PATCH 10/10] refactor --- src/core/src/bootstrap/Bootstrapper.py | 8 ++++---- src/core/tests/Test_Bootstrapper.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/src/bootstrap/Bootstrapper.py b/src/core/src/bootstrap/Bootstrapper.py index ed8f4c42..080b9439 100644 --- a/src/core/src/bootstrap/Bootstrapper.py +++ b/src/core/src/bootstrap/Bootstrapper.py @@ -143,12 +143,12 @@ def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): sudo_status = self.check_sudo_status(raise_if_not_sudo=raise_if_not_sudo) if sudo_status and attempts >= 1: - self.composite_logger.log_debug("[BST] Sudo status check completed successfully.[Attempt(s)={0}][MaxAttempt(s)={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) + self.composite_logger.log_debug("[BST] Sudo status check completed successfully. [Attempt(s)={0}][MaxAttempt(s)={1}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) return sudo_status elif sudo_status is None or sudo_status is False: if attempts < Constants.MAX_CHECK_SUDO_ATTEMPTS: - self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay.[ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay. [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) continue @@ -157,10 +157,10 @@ def check_sudo_status_with_attempts(self, raise_if_not_sudo=True): except Exception as exception: if attempts >= Constants.MAX_CHECK_SUDO_ATTEMPTS: - self.composite_logger.log_error("[BST] Customer environment error (sudo failure).[Exception={0}][Attempt(s)={1}][MaxAttempt(s)={2}]".format(str(exception), str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS)) + self.composite_logger.log_error("[BST] Customer environment error (sudo failure). [Attempt(s)={0}][MaxAttempt(s)={1}][Exception={2}]".format(str(attempts), Constants.MAX_CHECK_SUDO_ATTEMPTS, str(exception))) if raise_if_not_sudo: raise - self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay.[ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) + self.composite_logger.log_debug("[BST] Re-attempt sudo status check after a delay. [ElapsedTimeInSeconds={0}][Attempt(s)={1}]".format(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC, str(attempts))) time.sleep(Constants.MAX_CHECK_SUDO_INTERVAL_IN_SEC) def check_sudo_status(self, raise_if_not_sudo=True): diff --git a/src/core/tests/Test_Bootstrapper.py b/src/core/tests/Test_Bootstrapper.py index 7ccd14c4..be8a64a8 100644 --- a/src/core/tests/Test_Bootstrapper.py +++ b/src/core/tests/Test_Bootstrapper.py @@ -68,7 +68,7 @@ def mock_run_command_output_with_attempts(self, command, no_output=False, chk_er if self.sudo_check_status_attempts <= 2: return (1, "[sudo] password for user:\nFalse") - # Mock success (True) on the 3rd attempts + # Mock success (True) on the 3rd attempt elif self.sudo_check_status_attempts == 3: return (0, "uid=0(root) gid=0(root) groups=0(root)\nTrue") @@ -123,11 +123,11 @@ def test_check_sudo_status_unexpected_output_lines(self): # Verify exception msg contains the expected failure text self.assertTrue("Unexpected sudo check result" in str(context.exception)) - def test_check_sudo_status_succeeds_on_third_attempts(self): + def test_check_sudo_status_succeeds_on_third_attempt(self): # Test check sudo status after 2 failed attempts followed by success (true) self.runtime.env_layer.run_command_output = self.mock_run_command_output_with_attempts - # Attempt to check sudo status, succeed (true) on the 3rd attempts + # Attempt to check sudo status, succeed (true) on the 3rd attempt result = self.runtime.bootstrapper.check_sudo_status_with_attempts(raise_if_not_sudo=True) # Verify the result is success (True)