From ca0a1b1eb0b65f911d02233e98a7e34e3be52f4b Mon Sep 17 00:00:00 2001 From: juanbe Date: Fri, 19 Sep 2025 23:07:34 +0000 Subject: [PATCH 01/27] 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 02/27] 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 03/27] 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 04/27] 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 05/27] 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 f9a33289f2040dfe7a30bbc6cf8ab1db52302a51 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:07:05 +0000 Subject: [PATCH 06/27] add localdnsconfig folder --- .../localdnsconfig/disabled_mode_only.json | 3 ++ .../data/localdnsconfig/localdnsconfig.json | 47 +++++++++++++++++++ .../localdnsconfig/preferred_mode_only.json | 3 ++ .../localdnsconfig/required_mode_only.json | 3 ++ ...equired_mode_with_valid_dns_overrides.json | 47 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/disabled_mode_only.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_only.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/disabled_mode_only.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/disabled_mode_only.json new file mode 100644 index 00000000000..cbea7560aaf --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/disabled_mode_only.json @@ -0,0 +1,3 @@ +{ + "mode": "Disabled" +} \ No newline at end of file diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json new file mode 100644 index 00000000000..6af221b342f --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json @@ -0,0 +1,47 @@ +{ + "mode": "Required", + "kubeDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} \ No newline at end of file diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json new file mode 100644 index 00000000000..626704d89e3 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json @@ -0,0 +1,3 @@ +{ + "mode": "Preferred" +} \ No newline at end of file diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_only.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_only.json new file mode 100644 index 00000000000..81adb8f0432 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_only.json @@ -0,0 +1,3 @@ +{ + "mode": "Required" +} \ No newline at end of file diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json new file mode 100644 index 00000000000..6af221b342f --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json @@ -0,0 +1,47 @@ +{ + "mode": "Required", + "kubeDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} \ No newline at end of file From eb1363ba019f8f21b4c3df86b54e6cae9d8ec7e7 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:06:25 +0000 Subject: [PATCH 07/27] add more tests --- .../tests/latest/data/localdnsconfig.json | 47 ------- .../tests/latest/test_aks_commands.py | 128 +++++++++++++++++- 2 files changed, 126 insertions(+), 49 deletions(-) delete mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig.json deleted file mode 100644 index ba3170475f6..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "mode": "Required", - "kubeDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Verify", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - }, - "vnetDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "VnetDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Verify", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - } -} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 7e4041d897f..6323e6f6f12 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3346,7 +3346,7 @@ def test_aks_create_add_nodepool_with_custom_ca_trust_certificates( def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, @@ -3392,7 +3392,7 @@ def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_gr def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, @@ -3440,6 +3440,130 @@ def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource checks=[self.is_empty()], ) + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_localdns_required_to_disabled(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + disabled_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "disabled_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path, + "disabled_config": disabled_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has required mode and DNS overrides + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Update nodepool to disabled mode + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={disabled_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has disabled mode and no DNS overrides + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Disabled" + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_localdns_disabled_to_required(self, resource_group): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + disabled_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "disabled_mode_only.json") + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "disabled_config": disabled_config_path, + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with disabled mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={disabled_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has disabled mode and no DNS overrides + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Disabled" + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + + # Update nodepool to required mode + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has required mode and DNS overrides + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer( random_name_length=17, name_prefix="clitest", location="westus2" From 0fd5c83dd5ddbe29be41d67d341198d1e21dd8e6 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:57:43 +0000 Subject: [PATCH 08/27] add new json files --- .../defaulted_overrides_config.json | 46 +++++++++++++++++++ .../latest/data/localdnsconfig/empty.json | 1 + .../data/localdnsconfig/empty_mode.json | 3 ++ .../data/localdnsconfig/invalid_mode.json | 3 ++ .../data/localdnsconfig/missing_mode.json | 26 +++++++++++ .../latest/data/localdnsconfig/null.json | 1 + .../latest/data/localdnsconfig/null_mode.json | 3 ++ .../required_mode_empty_overrides.json | 5 ++ .../required_mode_extra_property.json | 16 +++++++ .../required_mode_invalid_kubedns.json | 8 ++++ .../required_mode_invalid_vnetdns.json | 8 ++++ .../required_mode_partial_invalid.json | 18 ++++++++ .../required_mode_with_valid_kubedns.json | 25 ++++++++++ .../required_mode_with_valid_vnetdns.json | 25 ++++++++++ 14 files changed, 188 insertions(+) create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty_mode.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/invalid_mode.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/missing_mode.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null_mode.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_empty_overrides.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_extra_property.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_kubedns.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_vnetdns.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_partial_invalid.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_kubedns.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_vnetdns.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json new file mode 100644 index 00000000000..a47eb490c71 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json @@ -0,0 +1,46 @@ +{ + "kubeDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty.json @@ -0,0 +1 @@ +{} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty_mode.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty_mode.json new file mode 100644 index 00000000000..b12fbfe5f68 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/empty_mode.json @@ -0,0 +1,3 @@ +{ + "mode": "" +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/invalid_mode.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/invalid_mode.json new file mode 100644 index 00000000000..2dbe61552c6 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/invalid_mode.json @@ -0,0 +1,3 @@ +{ + "mode": "InvalidMode" +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/missing_mode.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/missing_mode.json new file mode 100644 index 00000000000..e1318790539 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/missing_mode.json @@ -0,0 +1,26 @@ +{ + "kubeDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json new file mode 100644 index 00000000000..19765bd501b --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json @@ -0,0 +1 @@ +null diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null_mode.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null_mode.json new file mode 100644 index 00000000000..d5062c886e2 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null_mode.json @@ -0,0 +1,3 @@ +{ + "mode": null +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_empty_overrides.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_empty_overrides.json new file mode 100644 index 00000000000..5ca13a1f6d3 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_empty_overrides.json @@ -0,0 +1,5 @@ +{ + "mode": "Required", + "kubeDnsOverrides": {}, + "vnetDnsOverrides": {} +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_extra_property.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_extra_property.json new file mode 100644 index 00000000000..2b7a342ab6f --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_extra_property.json @@ -0,0 +1,16 @@ +{ + "mode": "Required", + "extraProperty": "unexpected", + "kubeDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_kubedns.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_kubedns.json new file mode 100644 index 00000000000..e35a1e0107f --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_kubedns.json @@ -0,0 +1,8 @@ +{ + "mode": "Required", + "kubeDnsOverrides": { + ".": { + "invalidField": 123 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_vnetdns.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_vnetdns.json new file mode 100644 index 00000000000..8db6ca61076 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_invalid_vnetdns.json @@ -0,0 +1,8 @@ +{ + "mode": "Required", + "vnetDnsOverrides": { + ".": { + "invalidField": 456 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_partial_invalid.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_partial_invalid.json new file mode 100644 index 00000000000..3c3af53e6ff --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_partial_invalid.json @@ -0,0 +1,18 @@ +{ + "mode": "Required", + "kubeDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDnsOverrides": { + ".": "invalid" + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_kubedns.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_kubedns.json new file mode 100644 index 00000000000..e6b8644adfa --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_kubedns.json @@ -0,0 +1,25 @@ +{ + "mode": "Required", + "kubeDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_vnetdns.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_vnetdns.json new file mode 100644 index 00000000000..63278c22aaf --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_vnetdns.json @@ -0,0 +1,25 @@ +{ + "mode": "Required", + "vnetDnsOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} From 450d1b810a554336b97fe55f384ebc8e552cbe9c Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:59:58 +0000 Subject: [PATCH 09/27] add default dns overrides --- .../tests/latest/test_localdns_profile.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py b/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py index 5f5b8a41c35..6b4efd3bf6b 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py @@ -36,6 +36,29 @@ } } +kubeDnsOverridesExceptedDefault = { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } +} + vnetDnsOverridesExpected = { ".": { "cacheDurationInSeconds": 3600, @@ -59,6 +82,29 @@ } } +vnetDnsOverridesExpectedDefault = { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } +} + def assert_dns_overrides_equal(actual, expected): """Assert that all keys and subkeys in expected are present and equal in actual, case-insensitive for keys.""" # Lowercase all keys in actual and expected for comparison From e6474f3be12fdcab7a8895f5af1aea2315bb4fe1 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:00:12 +0000 Subject: [PATCH 10/27] add more test cases --- .../tests/latest/test_aks_commands.py | 901 +++++++++++++++++- 1 file changed, 897 insertions(+), 4 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 6323e6f6f12..73725f662d1 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -1,3 +1,896 @@ + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + one_valid_override_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_one_valid_override_each.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "one_valid_override_config": one_valid_override_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={one_valid_override_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + # Validate that only one valid override each is present + assert len(result["localDnsProfile"].get("kubeDnsOverrides", {})) == 1 + assert len(result["localDnsProfile"].get("vnetDnsOverrides", {})) == 1 + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_invalid_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_mode_config": invalid_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_null_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + null_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "null_config": null_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={null_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure or default behavior for null config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_empty_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "empty_config": empty_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={empty_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure or default behavior for empty config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_vnetdns_config": invalid_vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure due to invalid vnetDnsOverrides, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_kubedns_config": invalid_kubedns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure due to invalid kubeDnsOverrides, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_missing_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + missing_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "missing_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "missing_mode_config": missing_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={missing_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure or default behavior for missing mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_empty_overrides(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_overrides_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_empty_overrides.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_overrides_config": empty_overrides_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_overrides_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert result["localDnsProfile"].get("kubeDnsOverrides", {}) == {} + assert result["localDnsProfile"].get("vnetDnsOverrides", {}) == {} + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_partial_invalid(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + partial_invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_partial_invalid.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "partial_invalid_config": partial_invalid_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={partial_invalid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure due to partial invalid override, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_extra_property(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_extra_property.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "extra_property_config": extra_property_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={extra_property_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "extraProperty" not in result["localDnsProfile"] + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_valid_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "vnetdns_config": vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_valid_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "kubedns_config": kubedns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_empty_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_mode_config": empty_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure or default behavior for empty mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_null_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + null_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "null_mode_config": null_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={null_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure or default behavior for null mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_empty_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_config": empty_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure or default behavior for empty config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_null_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + null_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "null_config": null_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={null_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure or default behavior for null config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_invalid_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "invalid_mode_config": invalid_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={invalid_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for invalid mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "invalid_config": invalid_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config with invalid vnetDnsOverrides + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={invalid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure due to invalid vnetDnsOverrides, but command succeeded." + except Exception as ex: + # Expecting an error due to invalid override + assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "invalid_config": invalid_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config with invalid kubeDnsOverrides + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={invalid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure due to invalid kubeDnsOverrides, but command succeeded." + except Exception as ex: + # Expecting an error due to invalid override + assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_preferred_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + preferred_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "preferred_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "preferred_config": preferred_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with preferred mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={preferred_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Preferred" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpectedDefault) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpectedDefault) + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + # -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. @@ -5,7 +898,7 @@ import os import pty -import semver +import os import subprocess import tempfile import time @@ -28,7 +921,7 @@ from azure.core.exceptions import HttpResponseError from knack.util import CLIError -from .test_localdns_profile import assert_dns_overrides_equal, vnetDnsOverridesExpected, kubeDnsOverridesExpected +from .test_localdns_profile import assert_dns_overrides_equal, vnetDnsOverridesExpected, kubeDnsOverridesExpected, vnetDnsOverridesExpectedDefault, kubeDnsOverridesExpectedDefault def _get_test_data_file(filename): curr_dir = os.path.dirname(os.path.realpath(__file__)) @@ -3555,8 +4448,8 @@ def test_aks_nodepool_update_localdns_disabled_to_required(self, resource_group) # Verify nodepool has required mode and DNS overrides result = self.cmd(show_cmd).get_output_in_json() assert result["localDnsProfile"]["mode"] == "Required" - assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) - assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpectedDefault) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpectedDefault) # Clean up self.cmd( From f11d205bddf4d5bc8c2a914eb834bbfa2eaba20f Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:23:03 +0000 Subject: [PATCH 11/27] move tests around, move invalid cases to another file --- .../tests/latest/test_aks_commands.py | 1013 ++--------------- .../latest/test_aks_invalid_localdns_cases.py | 641 +++++++++++ 2 files changed, 761 insertions(+), 893 deletions(-) create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 73725f662d1..108c074e768 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -1,896 +1,3 @@ - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - one_valid_override_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_one_valid_override_each.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "one_valid_override_config": one_valid_override_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={one_valid_override_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - # Validate that only one valid override each is present - assert len(result["localDnsProfile"].get("kubeDnsOverrides", {})) == 1 - assert len(result["localDnsProfile"].get("vnetDnsOverrides", {})) == 1 - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_invalid_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "invalid_mode_config": invalid_mode_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={invalid_mode_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - try: - self.cmd(update_cmd) - assert False, "Expected failure for invalid mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_null_config(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - null_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "null_config": null_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={null_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - try: - self.cmd(update_cmd) - assert False, "Expected failure or default behavior for null config, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_empty_config(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "empty_config": empty_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={empty_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - try: - self.cmd(update_cmd) - assert False, "Expected failure or default behavior for empty config, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_vnetdns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "invalid_vnetdns_config": invalid_vnetdns_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={invalid_vnetdns_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - try: - self.cmd(update_cmd) - assert False, "Expected failure due to invalid vnetDnsOverrides, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_update_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") - invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_kubedns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "valid_config": valid_config_path, - "invalid_kubedns_config": invalid_kubedns_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - update_cmd = ( - "aks nodepool update --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --localdns-config={invalid_kubedns_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - ) - try: - self.cmd(update_cmd) - assert False, "Expected failure due to invalid kubeDnsOverrides, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_missing_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - missing_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "missing_mode.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "missing_mode_config": missing_mode_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={missing_mode_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure or default behavior for missing mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_empty_overrides(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - empty_overrides_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_empty_overrides.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "empty_overrides_config": empty_overrides_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={empty_overrides_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - assert result["localDnsProfile"].get("kubeDnsOverrides", {}) == {} - assert result["localDnsProfile"].get("vnetDnsOverrides", {}) == {} - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_partial_invalid(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - partial_invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_partial_invalid.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "partial_invalid_config": partial_invalid_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={partial_invalid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure due to partial invalid override, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_extra_property(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_extra_property.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "extra_property_config": extra_property_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={extra_property_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - assert "extraProperty" not in result["localDnsProfile"] - assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_valid_vnetdns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "vnetdns_config": vnetdns_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={vnetdns_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None - assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_valid_kubedns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "kubedns_config": kubedns_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={kubedns_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None - assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_empty_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - empty_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty_mode.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "empty_mode_config": empty_mode_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={empty_mode_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure or default behavior for empty mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_null_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - null_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null_mode.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "null_mode_config": null_mode_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={null_mode_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure or default behavior for null mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_empty_config(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "empty_config": empty_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={empty_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure or default behavior for empty config, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "empty" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_null_config(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - null_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "null_config": null_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={null_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure or default behavior for null config, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "null" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_invalid_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "invalid_mode_config": invalid_mode_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={invalid_mode_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure for invalid mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_vnetdns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "invalid_config": invalid_config_path - }) - - # Create AKS cluster - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Add nodepool with required mode localdns config with invalid vnetDnsOverrides - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={invalid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure due to invalid vnetDnsOverrides, but command succeeded." - except Exception as ex: - # Expecting an error due to invalid override - assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" - - # Clean up - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_invalid_kubedns.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "invalid_config": invalid_config_path - }) - - # Create AKS cluster - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Add nodepool with required mode localdns config with invalid kubeDnsOverrides - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={invalid_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure due to invalid kubeDnsOverrides, but command succeeded." - except Exception as ex: - # Expecting an error due to invalid override - assert "invalid" in str(ex).lower() or "error" in str(ex).lower(), f"Unexpected error: {ex}" - - # Clean up - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_preferred_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - preferred_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "preferred_mode_only.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "preferred_config": preferred_config_path - }) - - # Create AKS cluster - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Add nodepool with preferred mode localdns config - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={preferred_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Show nodepool and check localDNSProfile - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Preferred" - assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpectedDefault) - assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpectedDefault) - # Clean up - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - - - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "required_config": required_config_path - }) - - # Create AKS cluster - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Add nodepool with required mode localdns config - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Show nodepool and check localDNSProfile - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) - assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None - assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None - # Clean up - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - # -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. @@ -4333,6 +3440,126 @@ def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource checks=[self.is_empty()], ) + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_dns_overrides_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") + vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_dns_overrides": valid_dns_overrides_path, + "kubedns_config": kubedns_config_path, + "vnetdns_config": vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with valid DNS overrides + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_dns_overrides} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + # Should have both kubeDnsOverrides and vnetDnsOverrides + assert_dns_overrides_equal(result["localDnsProfile"].get("kubeDnsOverrides", {}), kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"].get("vnetDnsOverrides", {}), vnetDnsOverridesExpected) + + # Update with only kubeDnsOverrides + update_kubedns_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_kubedns_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + + # Update with only vnetDnsOverrides + update_vnetdns_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_vnetdns_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") def test_aks_nodepool_update_localdns_required_to_disabled(self, resource_group, resource_group_location): diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py new file mode 100644 index 00000000000..f8b3b542010 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py @@ -0,0 +1,641 @@ +# -------------------------------------------------------------------------------------------- +# INVALID CASES FOR LOCAL DNS CONFIG TESTS +# These tests cover invalid, error, and edge-case scenarios for local DNS config handling. +# -------------------------------------------------------------------------------------------- + +import os +from .test_localdns_profile import assert_dns_overrides_equal, vnetDnsOverridesExpected, kubeDnsOverridesExpected +from azext_aks_preview.tests.latest.custom_preparers import AKSCustomResourceGroupPreparer +from azure.cli.testsdk.scenario_tests import AllowLargeResponse + +class InvalidLocalDnsConfigTests: + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_invalid_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_mode_config": invalid_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_empty_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty_config.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_config": empty_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={empty_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for empty config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "config" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_vnetdns_config": invalid_vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid vnetdns, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "vnetdns" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_kubedns_config": invalid_kubedns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid kubedns, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "kubedns" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_missing_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + missing_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "missing_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "missing_mode_config": missing_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={missing_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for missing mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_empty_overrides(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_overrides_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_empty_overrides.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_overrides_config": empty_overrides_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_overrides_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for empty overrides in required mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "overrides" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_partial_invalid(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + partial_invalid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_partial_invalid.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "partial_invalid_config": partial_invalid_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={partial_invalid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for partial invalid config in required mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "config" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_extra_property(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_extra_property.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "extra_property_config": extra_property_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={extra_property_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for extra property in required mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "property" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_empty_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_mode_config": empty_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for empty mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_null_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + null_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "null_mode_config": null_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={null_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for null mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_empty_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + empty_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "empty_config.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "empty_config": empty_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={empty_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for empty config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "config" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_null_config(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + null_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "null_config.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "null_config": null_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={null_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + try: + self.cmd(add_cmd) + assert False, "Expected failure for null config, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "config" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_invalid_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_mode_config": invalid_mode_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_mode_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid mode, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "mode" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_vnetdns_config": invalid_vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid vnetdns, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "vnetdns" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "valid_config": valid_config_path, + "invalid_kubedns_config": invalid_kubedns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={valid_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={invalid_kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + try: + self.cmd(update_cmd) + assert False, "Expected failure for invalid kubedns, but command succeeded." + except Exception as ex: + assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "kubedns" in str(ex).lower(), f"Unexpected error: {ex}" + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) From 3c6153b69990381fbaa28b333e366247dcd8b5e5 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:26:12 +0000 Subject: [PATCH 12/27] add back import semver --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 108c074e768..1d5aa94c0bc 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -5,7 +5,7 @@ import os import pty -import os +import semver import subprocess import tempfile import time From 8d63c84c8818858b72f200972388f8b5d7c84481 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Thu, 25 Sep 2025 22:02:38 +0000 Subject: [PATCH 13/27] reorder the existing tests --- .../tests/latest/test_aks_commands.py | 176 +++++++++++++----- 1 file changed, 130 insertions(+), 46 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 1d5aa94c0bc..a189cac9e0a 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3387,6 +3387,136 @@ def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_gr checks=[self.is_empty()], ) + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_single_vnetdns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "vnetdns_config": vnetdns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={vnetdns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_single_kubedns(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "kubedns_config": kubedns_config_path + }) + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={kubedns_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource_group_location): @@ -3514,52 +3644,6 @@ def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each checks=[self.is_empty()], ) - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_only.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "required_config": required_config_path - }) - - # Create AKS cluster - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Add nodepool with required mode localdns config - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - # Show nodepool and check localDNSProfile - show_cmd = ( - "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" - ) - result = self.cmd(show_cmd).get_output_in_json() - assert result["localDnsProfile"]["mode"] == "Required" - # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) - assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None - assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None - # Clean up - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") def test_aks_nodepool_update_localdns_required_to_disabled(self, resource_group, resource_group_location): From 5a5e8fac00b6a02518fc71f3d85a04f3d142a113 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 05:50:03 +0000 Subject: [PATCH 14/27] delete preferred mode only --- .../tests/latest/data/localdnsconfig/preferred_mode_only.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json deleted file mode 100644 index 626704d89e3..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/preferred_mode_only.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "mode": "Preferred" -} \ No newline at end of file From a77e76ed03387bead41bb2d5635d6ac385ea2f18 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 05:51:16 +0000 Subject: [PATCH 15/27] delete null.json --- .../azext_aks_preview/tests/latest/data/localdnsconfig/null.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json deleted file mode 100644 index 19765bd501b..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/null.json +++ /dev/null @@ -1 +0,0 @@ -null From 68e89e756951b9442193ad3209fa086fc3aa610a Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 06:04:21 +0000 Subject: [PATCH 16/27] remove redundant json file --- .../tests/latest/test_aks_commands.py | 8 ++++---- .../tests/latest/test_aks_invalid_localdns_cases.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index a189cac9e0a..69b0a55a869 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3346,7 +3346,7 @@ def test_aks_create_add_nodepool_with_custom_ca_trust_certificates( def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, @@ -3522,7 +3522,7 @@ def test_aks_nodepool_add_with_localdns_required_mode_single_kubedns(self, resou def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, @@ -3575,7 +3575,7 @@ def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_dns_overrides_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_dns_overrides_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") self.kwargs.update({ @@ -3649,7 +3649,7 @@ def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each def test_aks_nodepool_update_localdns_required_to_disabled(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") disabled_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "disabled_mode_only.json") self.kwargs.update({ "resource_group": resource_group, diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py index f8b3b542010..dcbf479604a 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py @@ -14,7 +14,7 @@ class InvalidLocalDnsConfigTests: def test_aks_nodepool_update_with_localdns_invalid_mode(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") self.kwargs.update({ "resource_group": resource_group, @@ -106,7 +106,7 @@ def test_aks_nodepool_update_with_localdns_empty_config(self, resource_group, re def test_aks_nodepool_update_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_vnetdns.json") self.kwargs.update({ "resource_group": resource_group, @@ -153,7 +153,7 @@ def test_aks_nodepool_update_with_localdns_required_mode_invalid_vnetdns(self, r def test_aks_nodepool_update_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_kubedns.json") self.kwargs.update({ "resource_group": resource_group, @@ -504,7 +504,7 @@ def test_aks_nodepool_add_with_localdns_null_config(self, resource_group, resour def test_aks_nodepool_add_with_localdns_invalid_mode(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_mode_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_mode.json") self.kwargs.update({ "resource_group": resource_group, @@ -551,7 +551,7 @@ def test_aks_nodepool_add_with_localdns_invalid_mode(self, resource_group, resou def test_aks_nodepool_add_with_localdns_required_mode_invalid_vnetdns(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_vnetdns.json") self.kwargs.update({ "resource_group": resource_group, @@ -598,7 +598,7 @@ def test_aks_nodepool_add_with_localdns_required_mode_invalid_vnetdns(self, reso def test_aks_nodepool_add_with_localdns_required_mode_invalid_kubedns(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_dns_overrides.json") + valid_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") invalid_kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "invalid_kubedns.json") self.kwargs.update({ "resource_group": resource_group, From 240b83e2b9d8a020bb0f1d3b862b5183745c0946 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 06:04:38 +0000 Subject: [PATCH 17/27] remove redundant json file --- ...equired_mode_with_valid_dns_overrides.json | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json deleted file mode 100644 index 6af221b342f..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/required_mode_with_valid_dns_overrides.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "mode": "Required", - "kubeDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Verify", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - }, - "vnetDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "VnetDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Verify", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - } -} \ No newline at end of file From 0ed586091d1ca5508fd352c2b08242b90acd3844 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:14:59 +0000 Subject: [PATCH 18/27] fix the mistake at line 3349 --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 69b0a55a869..3bb31163870 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3346,7 +3346,7 @@ def test_aks_create_add_nodepool_with_custom_ca_trust_certificates( def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, From 3f170651c6e28daa2b11bebb1a75ffcd17a0cfc2 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:37:39 +0000 Subject: [PATCH 19/27] forgot that i put all the configs in data/localconfig folder --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 3bb31163870..8cf3d3303f9 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3346,7 +3346,7 @@ def test_aks_create_add_nodepool_with_custom_ca_trust_certificates( def test_aks_nodepool_add_with_localdns_config(self, resource_group, resource_group_location): aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) - localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig.json") + localdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, @@ -3576,8 +3576,8 @@ def test_aks_nodepool_update_with_localdns_required_mode_one_valid_override_each aks_name = self.create_random_name("cliakstest", 16) nodepool_name = self.create_random_name("np", 6) valid_dns_overrides_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") - kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_kubedns.json") - vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_with_valid_vnetdns.json") + kubedns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig","required_mode_with_valid_kubedns.json") + vnetdns_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig","required_mode_with_valid_vnetdns.json") self.kwargs.update({ "resource_group": resource_group, "name": aks_name, From b43f7ad2d9db04bde9016a7091c600bc79ff8124 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:05:00 +0000 Subject: [PATCH 20/27] remove unused file --- .../defaulted_overrides_config.json | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json deleted file mode 100644 index a47eb490c71..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/defaulted_overrides_config.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "kubeDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - }, - "vnetDNSOverrides": { - ".": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "VnetDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "PreferUDP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - }, - "cluster.local": { - "cacheDurationInSeconds": 3600, - "forwardDestination": "ClusterCoreDNS", - "forwardPolicy": "Sequential", - "maxConcurrent": 1000, - "protocol": "ForceTCP", - "queryLogging": "Error", - "serveStale": "Immediate", - "serveStaleDurationInSeconds": 3600 - } - } -} From 8ac70f82f5422650616a92c4138a766417e924d9 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:35:49 +0000 Subject: [PATCH 21/27] spelling error --- .../azext_aks_preview/tests/latest/test_localdns_profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py b/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py index 6b4efd3bf6b..c6c1c06d84e 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_localdns_profile.py @@ -36,7 +36,7 @@ } } -kubeDnsOverridesExceptedDefault = { +kubeDnsOverridesExpectedDefault = { ".": { "cacheDurationInSeconds": 3600, "forwardDestination": "ClusterCoreDNS", From 8ce451ee3fa5f06f9731eaa7cd52cf400e861f8e Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:06:01 +0000 Subject: [PATCH 22/27] fix default dnsOverrides check when we create agentpool with required mode only --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 8cf3d3303f9..19c268b31d9 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3424,9 +3424,9 @@ def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, reso ) result = self.cmd(show_cmd).get_output_in_json() assert result["localDnsProfile"]["mode"] == "Required" - # Check for default DNS configuration (should not have kubeDnsOverrides or vnetDnsOverrides) - assert "kubeDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["kubeDnsOverrides"] is None - assert "vnetDnsOverrides" not in result["localDnsProfile"] or result["localDnsProfile"]["vnetDnsOverrides"] is None + + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) # Clean up self.cmd( "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", From 39e0ead699aa0b5245e0140973844aa96faa8488 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:09:03 +0000 Subject: [PATCH 23/27] restore localdnsconfig file --- .../tests/latest/data/localdnsconfig/localdnsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json index 6af221b342f..ba3170475f6 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig.json @@ -44,4 +44,4 @@ "serveStaleDurationInSeconds": 3600 } } -} \ No newline at end of file +} From 14d70aa2cf8d05d646ff4c132c25d152a4249382 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:33:12 +0000 Subject: [PATCH 24/27] delete extra property case from invalid test file --- .../latest/test_aks_invalid_localdns_cases.py | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py index dcbf479604a..1946bcedc78 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_invalid_localdns_cases.py @@ -309,44 +309,6 @@ def test_aks_nodepool_add_with_localdns_required_mode_partial_invalid(self, reso checks=[self.is_empty()], ) - @AllowLargeResponse() - @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") - def test_aks_nodepool_add_with_localdns_required_mode_extra_property(self, resource_group, resource_group_location): - aks_name = self.create_random_name("cliakstest", 16) - nodepool_name = self.create_random_name("np", 6) - extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_extra_property.json") - self.kwargs.update({ - "resource_group": resource_group, - "name": aks_name, - "nodepool_name": nodepool_name, - "ssh_key_value": self.generate_ssh_keys(), - "extra_property_config": extra_property_config_path - }) - - create_cmd = ( - "aks create --resource-group={resource_group} --name={name} " - "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " - "--kubernetes-version 1.33.0" - ) - self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) - - add_cmd = ( - "aks nodepool add --resource-group={resource_group} --cluster-name={name} " - "--name={nodepool_name} --node-count 1 --localdns-config={extra_property_config} " - "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " - "--kubernetes-version 1.33.0" - ) - try: - self.cmd(add_cmd) - assert False, "Expected failure for extra property in required mode, but command succeeded." - except Exception as ex: - assert "invalid" in str(ex).lower() or "error" in str(ex).lower() or "property" in str(ex).lower(), f"Unexpected error: {ex}" - - self.cmd( - "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", - checks=[self.is_empty()], - ) - @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") def test_aks_nodepool_add_with_localdns_empty_mode(self, resource_group, resource_group_location): From d73d85e326c1a572d3848a3c2543ff20b7cf2204 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:34:17 +0000 Subject: [PATCH 25/27] add extra property cases in src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py --- .../tests/latest/test_aks_commands.py | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 19c268b31d9..37e9dab528c 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3517,6 +3517,144 @@ def test_aks_nodepool_add_with_localdns_required_mode_single_kubedns(self, resou checks=[self.is_empty()], ) + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_required_mode_with_extra_property(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "required_mode_extra_property.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_config_with_extra_property(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig_with_extra_property.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_add_with_localdns_config_with_extra_property_in_dnsOverrides(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig_with_extra_property_in_dnsOverrides.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Show nodepool and check localDNSProfile + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") def test_aks_nodepool_update_with_localdns_config(self, resource_group, resource_group_location): @@ -3768,6 +3906,130 @@ def test_aks_nodepool_update_localdns_disabled_to_required(self, resource_group) checks=[self.is_empty()], ) + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_localdns_required_to_localdnsconfig_with_extra_property(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") + extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig_with_extra_property.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path, + "extra_property_config": extra_property_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has required mode and DNS overrides + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Update nodepool with extra property config + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={extra_property_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool still has required mode and DNS overrides (extra property should be ignored) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix="clitest", location="westus2") + def test_aks_nodepool_update_localdns_required_to_localdnsconfig_with_extra_property_in_dnsOverrides(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("np", 6) + required_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig.json") + extra_property_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data", "localdnsconfig", "localdnsconfig_with_extra_property_in_dnsOverrides.json") + self.kwargs.update({ + "resource_group": resource_group, + "name": aks_name, + "nodepool_name": nodepool_name, + "ssh_key_value": self.generate_ssh_keys(), + "required_config": required_config_path, + "extra_property_config": extra_property_config_path + }) + + # Create AKS cluster + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--node-count 1 --ssh-key-value={ssh_key_value} --generate-ssh-keys " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(create_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Add nodepool with required mode localdns config + add_cmd = ( + "aks nodepool add --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --node-count 1 --localdns-config={required_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + "--kubernetes-version 1.33.0" # k8s version > 1.33 to support localDNS + ) + self.cmd(add_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool has required mode and DNS overrides + show_cmd = ( + "aks nodepool show --resource-group={resource_group} --cluster-name={name} --name={nodepool_name}" + ) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Update nodepool with extra property config + update_cmd = ( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} " + "--name={nodepool_name} --localdns-config={extra_property_config} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/LocalDNSPreview " + ) + self.cmd(update_cmd, checks=[self.check("provisioningState", "Succeeded")]) + + # Verify nodepool still has required mode and DNS overrides (extra property should be ignored) + result = self.cmd(show_cmd).get_output_in_json() + assert result["localDnsProfile"]["mode"] == "Required" + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + + # Clean up + self.cmd( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer( random_name_length=17, name_prefix="clitest", location="westus2" From 9ff61c9a61d2450f0e804b277545585d9be3fdd4 Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:34:40 +0000 Subject: [PATCH 26/27] add extra property files --- .../localdnsconfig_with_extra_property.json | 48 +++++++++++++++++ ...g_with_extra_property_in_dnsOverrides.json | 52 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property.json create mode 100644 src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property_in_dnsOverrides.json diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property.json new file mode 100644 index 00000000000..83e1822cce1 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property.json @@ -0,0 +1,48 @@ +{ + "mode": "Required", + "extraProperty": "unexpected", + "kubeDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDNSOverrides": { + ".": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} diff --git a/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property_in_dnsOverrides.json b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property_in_dnsOverrides.json new file mode 100644 index 00000000000..a70eb30fb34 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/data/localdnsconfig/localdnsconfig_with_extra_property_in_dnsOverrides.json @@ -0,0 +1,52 @@ +{ + "mode": "Required", + "extraProperty": "unexpected", + "kubeDNSOverrides": { + ".": { + "extraProperty": "unexpected", + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "extraProperty": "unexpected", + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + }, + "vnetDNSOverrides": { + ".": { + "extraProperty": "unexpected", + "cacheDurationInSeconds": 3600, + "forwardDestination": "VnetDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "PreferUDP", + "queryLogging": "Error", + "serveStale": "Verify", + "serveStaleDurationInSeconds": 3600 + }, + "cluster.local": { + "extraProperty": "unexpected", + "cacheDurationInSeconds": 3600, + "forwardDestination": "ClusterCoreDNS", + "forwardPolicy": "Sequential", + "maxConcurrent": 1000, + "protocol": "ForceTCP", + "queryLogging": "Error", + "serveStale": "Immediate", + "serveStaleDurationInSeconds": 3600 + } + } +} From 734ad4cc33569d402cb248e6143fa0433603dc1c Mon Sep 17 00:00:00 2001 From: Saewon Kwak <23280628+saewoni@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:38:35 +0000 Subject: [PATCH 27/27] check for defaulted *dnsOverrides when making agent pool with mode: required only --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 37e9dab528c..9dfef3811fb 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3425,8 +3425,8 @@ def test_aks_nodepool_add_with_localdns_required_mode(self, resource_group, reso result = self.cmd(show_cmd).get_output_in_json() assert result["localDnsProfile"]["mode"] == "Required" - assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpected) - assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpected) + assert_dns_overrides_equal(result["localDnsProfile"]["kubeDnsOverrides"], kubeDnsOverridesExpectedDefault) + assert_dns_overrides_equal(result["localDnsProfile"]["vnetDnsOverrides"], vnetDnsOverridesExpectedDefault) # Clean up self.cmd( "aks delete --resource-group={resource_group} --name={name} --yes --no-wait",