From 0eeed5a7cbe597de4e7f3301e123693ca6670b92 Mon Sep 17 00:00:00 2001 From: Nageswara Nandigam <84482346+nagworld9@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:54:26 -0700 Subject: [PATCH] Fix bug with dependent extensions with no settings (#2285) (#2349) Co-authored-by: Laveesh Rohra --- azurelinuxagent/ga/exthandlers.py | 10 ++++-- ..._conf_dependencies_with_empty_settings.xml | 32 +++++++++++++++++++ tests/ga/test_extension.py | 32 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 tests/data/wire/ext_conf_dependencies_with_empty_settings.xml diff --git a/azurelinuxagent/ga/exthandlers.py b/azurelinuxagent/ga/exthandlers.py index eea295c814..29359800dc 100644 --- a/azurelinuxagent/ga/exthandlers.py +++ b/azurelinuxagent/ga/exthandlers.py @@ -563,9 +563,13 @@ def wait_for_handler_completion(handler_i, wait_until, extension=None): Check the status of the extension being handled. Wait until it has a terminal state or times out. :raises: Exception if it is not handled successfully. """ - extension_name = handler_i.get_extension_full_name(extension) + # If the handler had no settings, we should not wait at all for handler to report status. + if extension is None: + logger.info("No settings found for {0}, not waiting for it's status".format(extension_name)) + return + try: ext_completed, status = False, None @@ -1652,7 +1656,9 @@ def get_status_file_path(self, extension=None): def collect_ext_status(self, ext): self.logger.verbose("Collect extension status for {0}".format(self.get_extension_full_name(ext))) seq_no, ext_status_file = self.get_status_file_path(ext) - if seq_no == -1: + + # We should never try to read any status file if the handler has no settings, returning None in that case + if seq_no == -1 or ext is None: return None data = None diff --git a/tests/data/wire/ext_conf_dependencies_with_empty_settings.xml b/tests/data/wire/ext_conf_dependencies_with_empty_settings.xml new file mode 100644 index 0000000000..402de6438d --- /dev/null +++ b/tests/data/wire/ext_conf_dependencies_with_empty_settings.xml @@ -0,0 +1,32 @@ + + + + + Prod + + http://mock-goal-state/manifest_of_ga.xml + + + + Test + + http://mock-goal-state/manifest_of_ga.xml + + + + + + + + + + + + + + {"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]} + + + https://test.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo + diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py index cf6988108e..e5f110e356 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -994,6 +994,38 @@ def test_ext_handler_sequencing(self, *args): (dep_ext_level_5, ExtensionCommandNames.UNINSTALL) ) + def test_it_should_process_sequencing_properly_even_if_no_settings_for_dependent_extension( + self, mock_get, mock_crypt, *args): + test_data_file = DATA_FILE.copy() + test_data_file["ext_conf"] = "wire/ext_conf_dependencies_with_empty_settings.xml" + test_data = mockwiredata.WireProtocolData(test_data_file) + exthandlers_handler, protocol = self._create_mock(test_data, mock_get, mock_crypt, *args) + + ext_1 = extension_emulator(name="OSTCExtensions.ExampleHandlerLinux") + ext_2 = extension_emulator(name="OSTCExtensions.OtherExampleHandlerLinux") + + with enable_invocations(ext_1, ext_2) as invocation_record: + exthandlers_handler.run() + exthandlers_handler.report_ext_handlers_status() + + # Ensure no extension status was reported for OtherExampleHandlerLinux as no settings provided for it + self._assert_handler_status(protocol.report_vm_status, "Ready", 0, "1.0.0", + expected_handler_name="OSTCExtensions.OtherExampleHandlerLinux") + + # Ensure correct status reported back for the other extension with settings + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0", + expected_handler_name="OSTCExtensions.ExampleHandlerLinux") + self._assert_ext_status(protocol.report_vm_status, "success", 0, + expected_handler_name="OSTCExtensions.ExampleHandlerLinux") + + # Ensure the invocation order follows the dependency levels + invocation_record.compare( + (ext_2, ExtensionCommandNames.INSTALL), + (ext_2, ExtensionCommandNames.ENABLE), + (ext_1, ExtensionCommandNames.INSTALL), + (ext_1, ExtensionCommandNames.ENABLE) + ) + def test_ext_handler_sequencing_should_fail_if_handler_failed(self, mock_get, mock_crypt, *args): test_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE_EXT_SEQUENCING) exthandlers_handler, protocol = self._create_mock(test_data, mock_get, mock_crypt, *args)