Skip to content

Commit 898f772

Browse files
committed
Adding localdns profile to az aks nodepool cli
1 parent a469a8c commit 898f772

File tree

43 files changed

+20930
-63
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+20930
-63
lines changed

src/azure-cli/azure/cli/command_modules/acs/_help.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,6 +1806,9 @@
18061806
- name: --gateway-prefix-size
18071807
type: int
18081808
short-summary: The size of Public IPPrefix attached to the Gateway-mode node pool. The node pool must be in Gateway mode.
1809+
- name: --localdns-config
1810+
type: string
1811+
short-summary: Set the localDNS Profile for a nodepool with a JSON config file.
18091812
examples:
18101813
- name: Create a nodepool in an existing AKS cluster with ephemeral os enabled.
18111814
text: az aks nodepool add -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster --node-osdisk-type Ephemeral --node-osdisk-size 48
@@ -1955,6 +1958,9 @@
19551958
- name: --if-none-match
19561959
type: string
19571960
short-summary: Set to '*' to allow a new node pool to be created, but to prevent updating an existing node pool. Other values will be ignored.
1961+
- name: --localdns-config
1962+
type: string
1963+
short-summary: Set the localDNS Profile for a nodepool with a JSON config file.
19581964
examples:
19591965
- name: Reconcile the nodepool back to its current state.
19601966
text: az aks nodepool update -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster

src/azure-cli/azure/cli/command_modules/acs/_helpers.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,20 @@ def _get_id_from_shared_control_plane_identity(shared_identity) -> int:
321321
)
322322
)
323323
return 0
324+
325+
def process_dns_overrides(overrides_dict, target_dict, build_override_func):
326+
"""Helper function to safely process DNS overrides with null checks.
327+
Processes DNS override dictionaries from LocalDNS configuration,
328+
filtering out null values and applying the build function to valid entries.
329+
:param overrides_dict: Dictionary containing DNS overrides (can be None)
330+
:param target_dict: Target dictionary to populate with processed overrides
331+
:param build_override_func: Function to build override objects from dict values
332+
"""
333+
if not isinstance(overrides_dict, dict):
334+
raise InvalidArgumentValueError(
335+
f"Expected a dictionary for DNS overrides, but got {type(overrides_dict).__name__}: {overrides_dict}"
336+
)
337+
if overrides_dict is not None:
338+
for key, value in overrides_dict.items():
339+
if value is not None:
340+
target_dict[key] = build_override_func(value)

src/azure-cli/azure/cli/command_modules/acs/_params.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ def load_arguments(self, _):
972972
c.argument("if_none_match")
973973
c.argument('gpu_driver', arg_type=get_enum_type(gpu_driver_install_modes))
974974
c.argument("gateway_prefix_size", type=int, validator=validate_gateway_prefix_size)
975+
c.argument('localdns_config', help='Path to a JSON file to configure the local DNS profile for a new nodepool.')
975976

976977
with self.argument_context('aks nodepool update', resource_type=ResourceType.MGMT_CONTAINERSERVICE, operation_group='agent_pools') as c:
977978
c.argument('enable_cluster_autoscaler', options_list=[
@@ -1003,6 +1004,7 @@ def load_arguments(self, _):
10031004
c.argument('disable_secure_boot', action='store_true')
10041005
c.argument("if_match")
10051006
c.argument("if_none_match")
1007+
c.argument('localdns_config', help='Path to a JSON file to configure the local DNS profile for a new nodepool.')
10061008

10071009
with self.argument_context('aks nodepool upgrade') as c:
10081010
c.argument('max_surge', validator=validate_max_surge)

src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
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
3636
from azure.cli.command_modules.acs._validators import extract_comma_separated_string
3737
from azure.cli.command_modules.acs.base_decorator import BaseAKSContext, BaseAKSModels, BaseAKSParamDict
3838
from azure.cli.core import AzCommandsLoader
@@ -63,7 +63,6 @@
6363
# TODO:
6464
# 1. Add extra type checking for getter functions
6565

66-
6766
# pylint: disable=too-few-public-methods
6867
class 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
9391
class 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
10198
class 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+
16891781
class 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

22312329
class 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

Comments
 (0)