3232 DecoratorEarlyExitException ,
3333 DecoratorMode ,
3434)
35- from azure .cli .command_modules .acs ._helpers import get_snapshot_by_snapshot_id , safe_list_get
35+ from azure .cli .command_modules .acs ._helpers import get_snapshot_by_snapshot_id , safe_list_get , process_dns_overrides
3636from azure .cli .command_modules .acs ._validators import extract_comma_separated_string
3737from azure .cli .command_modules .acs .base_decorator import BaseAKSContext , BaseAKSModels , BaseAKSParamDict
3838from azure .cli .core import AzCommandsLoader
6363# TODO:
6464# 1. Add extra type checking for getter functions
6565
66-
6766# pylint: disable=too-few-public-methods
6867class AKSAgentPoolModels (BaseAKSModels ):
6968 """Store the models used in aks agentpool series of commands.
@@ -88,15 +87,13 @@ def __choose_agentpool_model_by_agentpool_decorator_mode(self):
8887 return self .ManagedClusterAgentPoolProfile
8988 return self .AgentPool
9089
91-
9290# pylint: disable=too-few-public-methods
9391class AKSAgentPoolParamDict (BaseAKSParamDict ):
9492 """Store the original parameters passed in by aks agentpool series of commands as an internal dictionary.
9593
9694 Only expose the "get" method externally to obtain parameter values, while recording usage.
9795 """
9896
99-
10097# pylint: disable=too-many-public-methods
10198class AKSAgentPoolContext (BaseAKSContext ):
10299 """Implement getter functions for all parameters in aks_agentpool_add and aks_agentpool_update.
@@ -1685,7 +1682,102 @@ def get_gateway_prefix_size(self) -> Union[int, None]:
16851682 """
16861683 return self .raw_param .get ('gateway_prefix_size' )
16871684
1685+ def get_localdns_config (self ):
1686+ return self .raw_param .get ("localdns_config" )
16881687
1688+ def get_localdns_profile (self ):
1689+ """
1690+ Returns the local DNS profile dict if set, or None.
1691+ Only supports loading from --localdns-config (JSON file).
1692+ Assumes the input is always a string filename.
1693+ """
1694+ config = self .get_localdns_config ()
1695+ if config :
1696+ if not isinstance (config , str ) or not os .path .isfile (config ):
1697+ raise InvalidArgumentValueError (
1698+ f"{ config } is not a valid file, or not accessible."
1699+ )
1700+ profile = get_file_json (config )
1701+ if not isinstance (profile , dict ):
1702+ raise InvalidArgumentValueError (
1703+ f"Error reading local DNS config from { config } . "
1704+ "Please provide a valid JSON file."
1705+ )
1706+ return profile
1707+ return None
1708+
1709+ def build_localdns_profile (self , agentpool : AgentPool ) -> AgentPool :
1710+ """Build local DNS profile for the AgentPool object if provided via --localdns-config."""
1711+ localdns_profile = self .get_localdns_profile ()
1712+ kube_dns_overrides , vnet_dns_overrides = None , None
1713+
1714+ if localdns_profile is not None :
1715+ def find_keys_case_insensitive (dictionary , target_keys ):
1716+ """Find multiple keys case-insensitively and return a dict mapping target_key -> actual_key"""
1717+ result = {}
1718+ lowered_keys = {key .lower (): key for key in dictionary .keys ()}
1719+ for target_key in target_keys :
1720+ lowered_target = target_key .lower ()
1721+ if lowered_target in lowered_keys :
1722+ result [target_key ] = lowered_keys [lowered_target ]
1723+ else :
1724+ result [target_key ] = None
1725+ return result
1726+
1727+ def build_override (override_dict ):
1728+ if not isinstance (override_dict , dict ):
1729+ raise InvalidArgumentValueError (
1730+ f"Expected a dictionary for DNS override settings,"
1731+ f" but got { type (override_dict ).__name__ } : { override_dict } "
1732+ )
1733+ camel_to_snake_case = {
1734+ "queryLogging" : "query_logging" ,
1735+ "protocol" : "protocol" ,
1736+ "forwardDestination" : "forward_destination" ,
1737+ "forwardPolicy" : "forward_policy" ,
1738+ "maxConcurrent" : "max_concurrent" ,
1739+ "cacheDurationInSeconds" : "cache_duration_in_seconds" ,
1740+ "serveStaleDurationInSeconds" : "serve_stale_duration_in_seconds" ,
1741+ "serveStale" : "serve_stale" ,
1742+ }
1743+ valid_keys = set (camel_to_snake_case .values ())
1744+ filtered = {}
1745+ for k , v in override_dict .items ():
1746+ if k in camel_to_snake_case :
1747+ filtered [camel_to_snake_case [k ]] = v
1748+ elif k in valid_keys :
1749+ filtered [k ] = v
1750+ return self .models .LocalDNSOverride (** filtered )
1751+
1752+ # Build kubeDNSOverrides and vnetDNSOverrides from the localdns_profile
1753+ key_mappings = find_keys_case_insensitive (localdns_profile , ["kubeDNSOverrides" , "vnetDNSOverrides" ])
1754+ actual_kube_key = key_mappings ["kubeDNSOverrides" ]
1755+ if actual_kube_key :
1756+ logger .debug ("Found kubeDNSOverrides key as: %s" , actual_kube_key )
1757+ kube_dns_overrides = {}
1758+ process_dns_overrides (
1759+ localdns_profile .get (actual_kube_key ),
1760+ kube_dns_overrides ,
1761+ build_override
1762+ )
1763+
1764+ actual_vnet_key = key_mappings ["vnetDNSOverrides" ]
1765+ if actual_vnet_key :
1766+ logger .debug ("Found vnetDNSOverrides key as: %s" , actual_vnet_key )
1767+ vnet_dns_overrides = {}
1768+ process_dns_overrides (
1769+ localdns_profile .get (actual_vnet_key ),
1770+ vnet_dns_overrides ,
1771+ build_override
1772+ )
1773+
1774+ agentpool .local_dns_profile = self .models .LocalDNSProfile (
1775+ mode = localdns_profile .get ("mode" ),
1776+ kube_dns_overrides = kube_dns_overrides ,
1777+ vnet_dns_overrides = vnet_dns_overrides ,
1778+ )
1779+ return agentpool
1780+
16891781class AKSAgentPoolAddDecorator :
16901782 def __init__ (
16911783 self ,
@@ -2186,6 +2278,8 @@ def construct_agentpool_profile_default(self, bypass_restore_defaults: bool = Fa
21862278 agentpool = self .set_up_agentpool_gateway_profile (agentpool )
21872279 # set up virtual machines profile
21882280 agentpool = self .set_up_virtual_machines_profile (agentpool )
2281+ # set up local DNS profile
2282+ agentpool = self .set_up_localdns_profile (agentpool )
21892283 # restore defaults
21902284 if not bypass_restore_defaults :
21912285 agentpool = self ._restore_defaults_in_agentpool (agentpool )
@@ -2227,6 +2321,10 @@ def add_agentpool(self, agentpool: AgentPool) -> AgentPool:
22272321 headers = self .context .get_aks_custom_headers (),
22282322 )
22292323
2324+ def set_up_localdns_profile (self , agentpool : AgentPool ) -> AgentPool :
2325+ """Set up local DNS profile for the AgentPool object if provided via --localdns-config."""
2326+ self ._ensure_agentpool (agentpool )
2327+ return self .context .build_localdns_profile (agentpool )
22302328
22312329class AKSAgentPoolUpdateDecorator :
22322330 def __init__ (
@@ -2513,11 +2611,12 @@ def update_agentpool_profile_default(self, agentpools: List[AgentPool] = None) -
25132611 agentpool = self .update_os_sku (agentpool )
25142612 # update fips image
25152613 agentpool = self .update_fips_image (agentpool )
2516-
25172614 # update vtpm
25182615 agentpool = self .update_vtpm (agentpool )
25192616 # update secure boot
25202617 agentpool = self .update_secure_boot (agentpool )
2618+ # update local DNS profile
2619+ agentpool = self .update_localdns_profile (agentpool )
25212620 return agentpool
25222621
25232622 def update_agentpool (self , agentpool : AgentPool ) -> AgentPool :
@@ -2553,3 +2652,8 @@ def update_agentpool(self, agentpool: AgentPool) -> AgentPool:
25532652 if_none_match = self .context .get_if_none_match (),
25542653 headers = self .context .get_aks_custom_headers (),
25552654 )
2655+
2656+ def update_localdns_profile (self , agentpool : AgentPool ) -> AgentPool :
2657+ """Update local DNS profile for the AgentPool object if provided via --localdns-config."""
2658+ self ._ensure_agentpool (agentpool )
2659+ return self .context .build_localdns_profile (agentpool )
0 commit comments