From ca0a1b1eb0b65f911d02233e98a7e34e3be52f4b Mon Sep 17 00:00:00 2001 From: juanbe Date: Fri, 19 Sep 2025 23:07:34 +0000 Subject: [PATCH 1/7] skip none overrides on localdns profile --- .../azext_aks_preview/agentpool_decorator.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/agentpool_decorator.py b/src/aks-preview/azext_aks_preview/agentpool_decorator.py index 6ec1fa1f3ea..0ba5c7ff4ca 100644 --- a/src/aks-preview/azext_aks_preview/agentpool_decorator.py +++ b/src/aks-preview/azext_aks_preview/agentpool_decorator.py @@ -1478,12 +1478,16 @@ def build_override(override_dict): # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile kube_overrides = localdns_profile.get("kubeDNSOverrides") - for key, value in kube_overrides.items(): - kube_dns_overrides[key] = build_override(value) + if kube_overrides is not None: + for key, value in kube_overrides.items(): + if value is not None: + kube_dns_overrides[key] = build_override(value) vnet_overrides = localdns_profile.get("vnetDNSOverrides") - for key, value in vnet_overrides.items(): - vnet_dns_overrides[key] = build_override(value) + if vnet_overrides is not None: + for key, value in vnet_overrides.items(): + if value is not None: + vnet_dns_overrides[key] = build_override(value) agentpool.local_dns_profile = self.models.LocalDNSProfile( mode=localdns_profile.get("mode"), @@ -1817,12 +1821,16 @@ def build_override(override_dict): # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile kube_overrides = localdns_profile.get("kubeDNSOverrides") - for key, value in kube_overrides.items(): - kube_dns_overrides[key] = build_override(value) + if kube_overrides is not None: + for key, value in kube_overrides.items(): + if value is not None: + kube_dns_overrides[key] = build_override(value) vnet_overrides = localdns_profile.get("vnetDNSOverrides") - for key, value in vnet_overrides.items(): - vnet_dns_overrides[key] = build_override(value) + if vnet_overrides is not None: + for key, value in vnet_overrides.items(): + if value is not None: + vnet_dns_overrides[key] = build_override(value) agentpool.local_dns_profile = self.models.LocalDNSProfile( mode=localdns_profile.get("mode"), From c044f0ed33f13d94b428b2215869ecddefcec5c0 Mon Sep 17 00:00:00 2001 From: juanbe Date: Fri, 19 Sep 2025 23:27:19 +0000 Subject: [PATCH 2/7] update history rst --- src/aks-preview/HISTORY.rst | 4 ++++ src/aks-preview/setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index d1fbaa6d3f0..5413b4e766c 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -11,10 +11,14 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ + +18.0.0b39 ++++++++ * Add machine command `az aks machine add` to add a machine to an existing machine pool. * Add blue-green upgrade strategy support for AKS node pools: - `az aks nodepool add/update/upgrade`: Add `--upgrade-strategy` parameter to switch between rolling and blue-green nodepool upgrades. - `az aks nodepool add/update/upgrade`: Add `--drain-batch-size`, `--drain-timeout-bg`, `--batch-soak-duration`, `--final-soak-duration` parameters to configure blue-green upgrade settings. +* Fix `--localdns-config` parameter to handle null values in JSON configuration files gracefully, preventing crashes when DNS override sections are null. 18.0.0b38 +++++++ diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 8e021e1ea7c..bc8c9863fc3 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "18.0.0b38" +VERSION = "18.0.0b39" CLASSIFIERS = [ "Development Status :: 4 - Beta", From 19c640440e0c24579dd38566e6bed86681761fae Mon Sep 17 00:00:00 2001 From: juanbe Date: Mon, 22 Sep 2025 20:53:23 +0000 Subject: [PATCH 3/7] refactor to process dns overrides func --- .../azext_aks_preview/agentpool_decorator.py | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/agentpool_decorator.py b/src/aks-preview/azext_aks_preview/agentpool_decorator.py index 0ba5c7ff4ca..5f9fdaeabb6 100644 --- a/src/aks-preview/azext_aks_preview/agentpool_decorator.py +++ b/src/aks-preview/azext_aks_preview/agentpool_decorator.py @@ -1448,6 +1448,13 @@ def set_up_managed_system_mode(self, agentpool: AgentPool) -> AgentPool: return agentpool + def _process_dns_overrides(self, overrides_dict, target_dict, build_override_func): + """Helper method to safely process DNS overrides with null checks.""" + if overrides_dict is not None: + for key, value in overrides_dict.items(): + if value is not None: + target_dict[key] = build_override_func(value) + def set_up_localdns_profile(self, agentpool: AgentPool) -> AgentPool: """Set up local DNS profile for the AgentPool object if provided via --localdns-config.""" self._ensure_agentpool(agentpool) @@ -1477,17 +1484,16 @@ def build_override(override_dict): return self.models.LocalDNSOverride(**filtered) # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile - kube_overrides = localdns_profile.get("kubeDNSOverrides") - if kube_overrides is not None: - for key, value in kube_overrides.items(): - if value is not None: - kube_dns_overrides[key] = build_override(value) - - vnet_overrides = localdns_profile.get("vnetDNSOverrides") - if vnet_overrides is not None: - for key, value in vnet_overrides.items(): - if value is not None: - vnet_dns_overrides[key] = build_override(value) + self._process_dns_overrides( + localdns_profile.get("kubeDNSOverrides"), + kube_dns_overrides, + build_override + ) + self._process_dns_overrides( + localdns_profile.get("vnetDNSOverrides"), + vnet_dns_overrides, + build_override + ) agentpool.local_dns_profile = self.models.LocalDNSProfile( mode=localdns_profile.get("mode"), @@ -1820,17 +1826,16 @@ def build_override(override_dict): return self.models.LocalDNSOverride(**filtered) # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile - kube_overrides = localdns_profile.get("kubeDNSOverrides") - if kube_overrides is not None: - for key, value in kube_overrides.items(): - if value is not None: - kube_dns_overrides[key] = build_override(value) - - vnet_overrides = localdns_profile.get("vnetDNSOverrides") - if vnet_overrides is not None: - for key, value in vnet_overrides.items(): - if value is not None: - vnet_dns_overrides[key] = build_override(value) + self._process_dns_overrides( + localdns_profile.get("kubeDNSOverrides"), + kube_dns_overrides, + build_override + ) + self._process_dns_overrides( + localdns_profile.get("vnetDNSOverrides"), + vnet_dns_overrides, + build_override + ) agentpool.local_dns_profile = self.models.LocalDNSProfile( mode=localdns_profile.get("mode"), From 2778aba7476d2ff212c205e0cb4d8fcfbadf889d Mon Sep 17 00:00:00 2001 From: juanbe Date: Mon, 22 Sep 2025 22:15:38 +0000 Subject: [PATCH 4/7] move overrides function to helper file --- src/aks-preview/azext_aks_preview/_helpers.py | 16 ++ .../azext_aks_preview/agentpool_decorator.py | 16 +- .../tests/latest/test_agentpool_decorator.py | 157 ++++++++++++++++++ 3 files changed, 178 insertions(+), 11 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_helpers.py b/src/aks-preview/azext_aks_preview/_helpers.py index 0e791b4aecf..04f39d104dd 100644 --- a/src/aks-preview/azext_aks_preview/_helpers.py +++ b/src/aks-preview/azext_aks_preview/_helpers.py @@ -448,3 +448,19 @@ def get_extension_in_allow_list(result): if _check_if_extension_type_is_in_allow_list(result.extension_type.lower()): return result return None + + +def process_dns_overrides(overrides_dict, target_dict, build_override_func): + """Helper function to safely process DNS overrides with null checks. + + Processes DNS override dictionaries from LocalDNS configuration, + filtering out null values and applying the build function to valid entries. + + :param overrides_dict: Dictionary containing DNS overrides (can be None) + :param target_dict: Target dictionary to populate with processed overrides + :param build_override_func: Function to build override objects from dict values + """ + if overrides_dict is not None: + for key, value in overrides_dict.items(): + if value is not None: + target_dict[key] = build_override_func(value) diff --git a/src/aks-preview/azext_aks_preview/agentpool_decorator.py b/src/aks-preview/azext_aks_preview/agentpool_decorator.py index 5f9fdaeabb6..2f627f13fac 100644 --- a/src/aks-preview/azext_aks_preview/agentpool_decorator.py +++ b/src/aks-preview/azext_aks_preview/agentpool_decorator.py @@ -50,6 +50,7 @@ from azext_aks_preview._helpers import ( get_nodepool_snapshot_by_snapshot_id, filter_hard_taints, + process_dns_overrides, ) logger = get_logger(__name__) @@ -1448,13 +1449,6 @@ def set_up_managed_system_mode(self, agentpool: AgentPool) -> AgentPool: return agentpool - def _process_dns_overrides(self, overrides_dict, target_dict, build_override_func): - """Helper method to safely process DNS overrides with null checks.""" - if overrides_dict is not None: - for key, value in overrides_dict.items(): - if value is not None: - target_dict[key] = build_override_func(value) - def set_up_localdns_profile(self, agentpool: AgentPool) -> AgentPool: """Set up local DNS profile for the AgentPool object if provided via --localdns-config.""" self._ensure_agentpool(agentpool) @@ -1484,12 +1478,12 @@ def build_override(override_dict): return self.models.LocalDNSOverride(**filtered) # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile - self._process_dns_overrides( + process_dns_overrides( localdns_profile.get("kubeDNSOverrides"), kube_dns_overrides, build_override ) - self._process_dns_overrides( + process_dns_overrides( localdns_profile.get("vnetDNSOverrides"), vnet_dns_overrides, build_override @@ -1826,12 +1820,12 @@ def build_override(override_dict): return self.models.LocalDNSOverride(**filtered) # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile - self._process_dns_overrides( + process_dns_overrides( localdns_profile.get("kubeDNSOverrides"), kube_dns_overrides, build_override ) - self._process_dns_overrides( + process_dns_overrides( localdns_profile.get("vnetDNSOverrides"), vnet_dns_overrides, build_override diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_agentpool_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_agentpool_decorator.py index 08b4de4d0a7..9591c00d1b6 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_agentpool_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_agentpool_decorator.py @@ -2684,6 +2684,151 @@ def common_update_blue_green_upgrade_settings(self): ) self.assertEqual(dec_agentpool_3, ground_truth_agentpool_3) + def common_update_localdns_profile(self): + import tempfile + import json + import os + + # Test case 1: LocalDNS config provided - verify method is called + localdns_config = { + "mode": "Required", + "kubeDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "protocol": "PreferUDP" + } + } + } + + with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".json") as f: + json.dump(localdns_config, f) + f.flush() + config_file_path = f.name + + try: + dec_1 = AKSPreviewAgentPoolUpdateDecorator( + self.cmd, + self.client, + {"localdns_config": config_file_path}, + self.resource_type, + self.agentpool_decorator_mode, + ) + + agentpool_1 = self.create_initialized_agentpool_instance() + dec_1.context.attach_agentpool(agentpool_1) + dec_agentpool_1 = dec_1.update_localdns_profile(agentpool_1) + + # Verify that LocalDNS profile was created and assigned + self.assertIsNotNone(dec_agentpool_1.local_dns_profile) + self.assertEqual(dec_agentpool_1.local_dns_profile.mode, "Required") + + finally: + os.unlink(config_file_path) + + # Test case 2: No LocalDNS config provided - no change + dec_2 = AKSPreviewAgentPoolUpdateDecorator( + self.cmd, + self.client, + {}, + self.resource_type, + self.agentpool_decorator_mode, + ) + + agentpool_2 = self.create_initialized_agentpool_instance() + original_local_dns_profile = agentpool_2.local_dns_profile + dec_2.context.attach_agentpool(agentpool_2) + dec_agentpool_2 = dec_2.update_localdns_profile(agentpool_2) + + # Verify LocalDNS profile wasn't changed + self.assertEqual(dec_agentpool_2.local_dns_profile, original_local_dns_profile) + + # Test case 3: LocalDNS config with null values + localdns_config_with_nulls = { + "mode": "Required", + "kubeDNSOverrides": None, + "vnetDNSOverrides": { + ".": { + "cacheDurationInSeconds": 1800, + "protocol": "ForceTCP" + } + } + } + + with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".json") as f: + json.dump(localdns_config_with_nulls, f) + f.flush() + config_file_path = f.name + + try: + dec_3 = AKSPreviewAgentPoolUpdateDecorator( + self.cmd, + self.client, + {"localdns_config": config_file_path}, + self.resource_type, + self.agentpool_decorator_mode, + ) + + agentpool_3 = self.create_initialized_agentpool_instance() + dec_3.context.attach_agentpool(agentpool_3) + dec_agentpool_3 = dec_3.update_localdns_profile(agentpool_3) + + # Verify that LocalDNS profile was created with null handling + self.assertIsNotNone(dec_agentpool_3.local_dns_profile) + self.assertEqual(dec_agentpool_3.local_dns_profile.mode, "Required") + # kubeDNSOverrides should be empty dict due to null input + self.assertEqual(len(dec_agentpool_3.local_dns_profile.kube_dns_overrides), 0) + # vnetDNSOverrides should have one entry + self.assertEqual(len(dec_agentpool_3.local_dns_profile.vnet_dns_overrides), 1) + + finally: + os.unlink(config_file_path) + + def common_test_process_dns_overrides_helper(self): + from azext_aks_preview._helpers import process_dns_overrides + + # Test the process_dns_overrides utility function functionality + + # Test case 1: Valid DNS overrides without nulls + dns_overrides = { + ".": { + "cacheDurationInSeconds": 3600, + "protocol": "PreferUDP" + } + } + target_dict = {} + + def mock_build_override(override_dict): + return self.models.LocalDNSOverride( + cache_duration_in_seconds=override_dict.get("cacheDurationInSeconds"), + protocol=override_dict.get("protocol") + ) + + process_dns_overrides(dns_overrides, target_dict, mock_build_override) + self.assertEqual(len(target_dict), 1) + self.assertIn(".", target_dict) + + # Test case 2: DNS overrides with null values (should handle gracefully) + dns_overrides_with_nulls = { + ".": { + "cacheDurationInSeconds": 1800, + "protocol": None + } + } + target_dict_2 = {} + + process_dns_overrides(dns_overrides_with_nulls, target_dict_2, mock_build_override) + self.assertEqual(len(target_dict_2), 1) + + # Test case 3: None input (should handle gracefully) + target_dict_3 = {} + process_dns_overrides(None, target_dict_3, mock_build_override) + self.assertEqual(len(target_dict_3), 0) + + # Test case 4: Empty input (should handle gracefully) + target_dict_4 = {} + process_dns_overrides({}, target_dict_4, mock_build_override) + self.assertEqual(len(target_dict_4), 0) + class AKSPreviewAgentPoolUpdateDecoratorStandaloneModeTestCase( AKSPreviewAgentPoolUpdateDecoratorCommonTestCase @@ -2721,6 +2866,12 @@ def test_update_upgrade_strategy(self): def test_update_blue_green_upgrade_settings(self): self.common_update_blue_green_upgrade_settings() + def test_update_localdns_profile(self): + self.common_update_localdns_profile() + + def test_process_dns_overrides_helper(self): + self.common_test_process_dns_overrides_helper() + def test_update_agentpool_profile_preview(self): import inspect @@ -2808,6 +2959,12 @@ def test_update_upgrade_strategy(self): def test_update_blue_green_upgrade_settings(self): self.common_update_blue_green_upgrade_settings() + def test_update_localdns_profile(self): + self.common_update_localdns_profile() + + def test_process_dns_overrides_helper(self): + self.common_test_process_dns_overrides_helper() + def test_update_agentpool_profile_preview(self): import inspect From 6d0dd1e0cf35490e7df759796c3329b8c4bdb9fa Mon Sep 17 00:00:00 2001 From: juanbe Date: Mon, 22 Sep 2025 23:04:08 +0000 Subject: [PATCH 5/7] apply linter suggestions --- src/aks-preview/azext_aks_preview/_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_helpers.py b/src/aks-preview/azext_aks_preview/_helpers.py index 04f39d104dd..9b3fdd3ed01 100644 --- a/src/aks-preview/azext_aks_preview/_helpers.py +++ b/src/aks-preview/azext_aks_preview/_helpers.py @@ -452,12 +452,12 @@ def get_extension_in_allow_list(result): def process_dns_overrides(overrides_dict, target_dict, build_override_func): """Helper function to safely process DNS overrides with null checks. - + Processes DNS override dictionaries from LocalDNS configuration, filtering out null values and applying the build function to valid entries. - + :param overrides_dict: Dictionary containing DNS overrides (can be None) - :param target_dict: Target dictionary to populate with processed overrides + :param target_dict: Target dictionary to populate with processed overrides :param build_override_func: Function to build override objects from dict values """ if overrides_dict is not None: From 56f1932ba796377fb8598421ea93d5175e979b29 Mon Sep 17 00:00:00 2001 From: juanbe Date: Wed, 24 Sep 2025 22:45:04 +0000 Subject: [PATCH 6/7] update version --- src/aks-preview/HISTORY.rst | 5 ++++- src/aks-preview/setup.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index c6b1074e8d8..7bc88afaf3c 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,6 +12,10 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +18.0.0b40 +++++++ +* Fix `--localdns-config` parameter to handle null values in JSON configuration files gracefully, preventing crashes when DNS override sections are null. + 18.0.0b39 +++++++ * Add option `AzureLinuxOSGuard` and `AzureLinux3OSGuard` to `--os-sku` for `az aks nodepool add` and `az aks nodepool update`. @@ -19,7 +23,6 @@ Pending * Add blue-green upgrade strategy support for AKS node pools: - `az aks nodepool add/update/upgrade`: Add `--upgrade-strategy` parameter to switch between rolling and blue-green nodepool upgrades. - `az aks nodepool add/update/upgrade`: Add `--drain-batch-size`, `--drain-timeout-bg`, `--batch-soak-duration`, `--final-soak-duration` parameters to configure blue-green upgrade settings. -* Fix `--localdns-config` parameter to handle null values in JSON configuration files gracefully, preventing crashes when DNS override sections are null. 18.0.0b38 +++++++ diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index bc8c9863fc3..3fc6651cc8a 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "18.0.0b39" +VERSION = "18.0.0b40" CLASSIFIERS = [ "Development Status :: 4 - Beta", From 121af4316840e3e938276bc8aceb38a8c57d9e25 Mon Sep 17 00:00:00 2001 From: FumingZhang <81607949+FumingZhang@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:34:42 +1000 Subject: [PATCH 7/7] Update src/aks-preview/HISTORY.rst --- src/aks-preview/HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 6aa41ee83a8..3ef5b6a14cb 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -14,7 +14,7 @@ Pending * Fix `--localdns-config` parameter to handle null values in JSON configuration files gracefully, preventing crashes when DNS override sections are null. 18.0.0b40 -++++++ ++++++++ * Add option `Windows2025` to `--os-sku` for `az aks nodepool add`. * `az aks create`: Add new parameter `--container-storage-version` to enable the given version of Azure Container Storage. * `az aks update`: Add new parameter `--container-storage-version` to enable the given version of Azure Container Storage.