diff --git a/azurelinuxagent/ga/exthandlers.py b/azurelinuxagent/ga/exthandlers.py index 9b5e7c0de8..f4248fd5e8 100644 --- a/azurelinuxagent/ga/exthandlers.py +++ b/azurelinuxagent/ga/exthandlers.py @@ -720,7 +720,8 @@ def handle_enable(self, ext_handler_i, extension): # failures back to CRP. If a placeholder for an extension already exists with Transitioning status, we would # not override it, hence we only create a placeholder for enable/disable commands but the extensions have the # data to create their own if needed. - ext_handler_i.create_placeholder_status_file(extension) + if ext_handler_i.should_create_default_placeholder(extension): + ext_handler_i.create_placeholder_status_file(extension) self.__handle_extension(ext_handler_i, extension, uninstall_exit_code) @staticmethod @@ -1389,6 +1390,17 @@ def initialize(self): CGroupConfigurator.get_instance().setup_extension_slice( extension_name=self.get_full_name()) + def should_create_default_placeholder(self, extension=None): + """ + There's a bug in the AKS extension where they dont update the status file if it exists. + This violates the contract we have with extensions. Until they fix their extension, + we're going to skip creating a placeholder for them to ensure they dont have any downtime. + For all other extensions, we should create a + """ + + ignore_extension_regex = r"Microsoft.AKS.Compute.AKS\S*" + return re.match(ignore_extension_regex, self.get_extension_full_name(extension)) is None + def create_placeholder_status_file(self, extension=None, status=ValidHandlerStatus.transitioning, code=0, operation="Enabling Extension", message="Install/Enable is in progress."): _, status_path = self.get_status_file_path(extension) diff --git a/tests/data/wire/ext_conf_aks_extension.xml b/tests/data/wire/ext_conf_aks_extension.xml new file mode 100644 index 0000000000..5901c0e442 --- /dev/null +++ b/tests/data/wire/ext_conf_aks_extension.xml @@ -0,0 +1,69 @@ + + + + + Prod + + http://mock-goal-state/manifest_of_ga.xml + + + + Test + + http://mock-goal-state/manifest_of_ga.xml + + + + + + + + + + + + + + { + "runtimeSettings": [ + { + "handlerSettings": { + "publicSettings": {"message": "Enabling non-AKS"} + } + } + ] + } + + + + + { + "runtimeSettings": [ + { + "handlerSettings": { + "publicSettings": {"message": "Enabling AKSNode"} + } + } + ] + } + + + + + { + "runtimeSettings": [ + { + "handlerSettings": { + "publicSettings": {"message": "Enabling AKSBilling"} + } + } + ] + } + + + + + 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 fb93bc3846..8d82e93cfc 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -1575,6 +1575,56 @@ def mock_popen(cmd, *args, **kwargs): expected_msg="Dependent Extension OSTCExtensions.OtherExampleHandlerLinux did not reach a terminal state within the allowed timeout. Last status was {0}".format( ValidHandlerStatus.warning)) + def test_it_should_not_create_placeholder_for_aks_extension(self, mock_http_get, mock_crypt_util, *args): + original_popen = subprocess.Popen + + def mock_popen(cmd, *_, **kwargs): + if 'env' in kwargs: + if ExtensionCommandNames.ENABLE not in cmd: + # To force the test extension to not create a status file on Install, changing command + return original_popen(["echo", "not-enable"], *_, **kwargs) + + seq_no = kwargs['env'][ExtCommandEnvVariable.ExtensionSeqNumber] + ext_path = kwargs['env'][ExtCommandEnvVariable.ExtensionPath] + status_file_name = "{0}.status".format(seq_no) + status_file = os.path.join(ext_path, "status", status_file_name) + if "AKS" in cmd: + self.assertFalse(os.path.exists(status_file), "Placeholder file should not be created for AKS") + else: + self.assertTrue(os.path.exists(status_file), "Placeholder file should be created for all extensions") + + return original_popen(cmd, *_, **kwargs) + + aks_test_mock = DATA_FILE.copy() + aks_test_mock["ext_conf"] = "wire/ext_conf_aks_extension.xml" + + exthandlers_handler, protocol = self._create_mock(mockwiredata.WireProtocolData(aks_test_mock), + mock_http_get, mock_crypt_util, *args) + + with patch('azurelinuxagent.common.cgroupapi.subprocess.Popen', side_effect=mock_popen): + exthandlers_handler.run() + exthandlers_handler.report_ext_handlers_status() + + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0", + expected_handler_name="OSTCExtensions.ExampleHandlerLinux") + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0", + expected_handler_name="Microsoft.AKS.Compute.AKS.Linux.AKSNode") + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0", + expected_handler_name="Microsoft.AKS.Compute.AKS-Engine.Linux.Billing") + # Extension without settings + self._assert_handler_status(protocol.report_vm_status, "Ready", 0, "1.0.0", + expected_handler_name="Microsoft.AKS.Compute.AKS.Linux.Billing") + + self._assert_ext_status(protocol.report_vm_status, ValidHandlerStatus.success, 0, + expected_handler_name="OSTCExtensions.ExampleHandlerLinux", + expected_msg="Enabling non-AKS") + self._assert_ext_status(protocol.report_vm_status, ValidHandlerStatus.success, 0, + expected_handler_name="Microsoft.AKS.Compute.AKS.Linux.AKSNode", + expected_msg="Enabling AKSNode") + self._assert_ext_status(protocol.report_vm_status, ValidHandlerStatus.success, 0, + expected_handler_name="Microsoft.AKS.Compute.AKS-Engine.Linux.Billing", + expected_msg="Enabling AKSBilling") + def test_it_should_include_part_of_status_in_ext_handler_message(self, mock_http_get, mock_crypt_util, *args): """ Testing scenario when the status file is invalid,