Skip to content

Commit 6ee7ce1

Browse files
committed
Address comments
1 parent c349397 commit 6ee7ce1

File tree

6 files changed

+241
-36
lines changed

6 files changed

+241
-36
lines changed

src/aks-preview/HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ To release a new version, please select a new version number (usually plus 1 to
1212
Pending
1313
+++++++
1414

15+
19.0.0b15
16+
+++++++
17+
* Fix `NoneType` error when performing operations on automatic clusters that have hosted system components enabled.
18+
19+
1520
19.0.0b14
1621
+++++++
1722
* `az aks safeguards`: Add support for Deployment Safeguards with Pod Security Standards (PSS). New `--pss-level` parameter allows setting PSS enforcement level to Privileged, Baseline, or Restricted. Commands now support both `-g/-n` and `--cluster` argument patterns.

src/aks-preview/azext_aks_preview/custom.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,8 +1578,8 @@ def aks_scale(cmd, # pylint: disable=unused-argument
15781578
"Please specify nodepool name or use az aks nodepool command to scale node pool"
15791579
)
15801580

1581-
for agent_profile in instance.agent_pool_profiles:
1582-
if agent_profile.name == nodepool_name or (nodepool_name == "" and len(instance.agent_pool_profiles) == 1):
1581+
for agent_profile in (instance.agent_pool_profiles or []):
1582+
if agent_profile.name == nodepool_name or (nodepool_name == "" and instance.agent_pool_profiles and len(instance.agent_pool_profiles) == 1):
15831583
if agent_profile.enable_auto_scaling:
15841584
raise CLIError(
15851585
"Cannot scale cluster autoscaler enabled node pool.")
@@ -1629,7 +1629,7 @@ def aks_upgrade(cmd,
16291629
_fill_defaults_for_pod_identity_profile(instance.pod_identity_profile)
16301630

16311631
vmas_cluster = False
1632-
for agent_profile in instance.agent_pool_profiles:
1632+
for agent_profile in (instance.agent_pool_profiles or []):
16331633
if agent_profile.type.lower() == "availabilityset":
16341634
vmas_cluster = True
16351635
break
@@ -1646,7 +1646,7 @@ def aks_upgrade(cmd,
16461646

16471647
# This only provide convenience for customer at client side so they can run az aks upgrade to upgrade all
16481648
# nodepools of a cluster. The SDK only support upgrade single nodepool at a time.
1649-
for agent_pool_profile in instance.agent_pool_profiles:
1649+
for agent_pool_profile in (instance.agent_pool_profiles or []):
16501650
if vmas_cluster:
16511651
raise CLIError('This cluster is not using VirtualMachineScaleSets. Node image upgrade only operation '
16521652
'can only be applied on VirtualMachineScaleSets and VirtualMachines(Preview) cluster.')
@@ -1715,7 +1715,7 @@ def aks_upgrade(cmd,
17151715
return None
17161716

17171717
if upgrade_all:
1718-
for agent_profile in instance.agent_pool_profiles:
1718+
for agent_profile in (instance.agent_pool_profiles or []):
17191719
agent_profile.orchestrator_version = kubernetes_version
17201720
agent_profile.creation_data = None
17211721

@@ -3044,12 +3044,13 @@ def aks_enable_addons(
30443044
if enable_virtual_node:
30453045
# All agent pool will reside in the same vnet, we will grant vnet level Contributor role
30463046
# in later function, so using a random agent pool here is OK
3047-
random_agent_pool = result.agent_pool_profiles[0]
3048-
if random_agent_pool.vnet_subnet_id != "":
3049-
add_virtual_node_role_assignment(
3050-
cmd, result, random_agent_pool.vnet_subnet_id)
3051-
# Else, the cluster is not using custom VNet, the permission is already granted in AKS RP,
3052-
# we don't need to handle it in client side in this case.
3047+
if result.agent_pool_profiles and len(result.agent_pool_profiles) > 0:
3048+
random_agent_pool = result.agent_pool_profiles[0]
3049+
if random_agent_pool.vnet_subnet_id != "":
3050+
add_virtual_node_role_assignment(
3051+
cmd, result, random_agent_pool.vnet_subnet_id)
3052+
# Else, the cluster is not using custom VNet, the permission is already granted in AKS RP,
3053+
# we don't need to handle it in client side in this case.
30533054

30543055
else:
30553056
result = sdk_no_wait(no_wait, client.begin_create_or_update,
@@ -4470,6 +4471,9 @@ def aks_check_network_outbound(
44704471
if not cluster:
44714472
raise ValidationError("Can not get cluster information!")
44724473

4474+
if not cluster.agent_pool_profiles or len(cluster.agent_pool_profiles) == 0:
4475+
raise ValidationError("No agent pool profiles found in the cluster!")
4476+
44734477
vm_set_type = cluster.agent_pool_profiles[0].type
44744478
if not vm_set_type:
44754479
raise ValidationError("Can not get VM set type of the cluster!")

src/aks-preview/azext_aks_preview/managed_cluster_decorator.py

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5201,6 +5201,45 @@ def check_raw_parameters(self):
52015201
error_msg = f"Please specify one or more of {' or '.join(option_names)}."
52025202
raise RequiredArgumentMissingError(error_msg)
52035203

5204+
def update_agentpool_profile(self, mc: ManagedCluster) -> ManagedCluster:
5205+
"""Update agentpool profile for the ManagedCluster object.
5206+
5207+
Preview override to handle empty agent_pool_profiles gracefully.
5208+
5209+
:return: the ManagedCluster object
5210+
"""
5211+
self._ensure_mc(mc)
5212+
5213+
# Preview-specific change: an AKS ManagedCluster of automatic
5214+
# cluster with hosted system components may not have agent pools
5215+
# When transitioning from hosted to non-hosted automatic clusters,
5216+
# customers must first add a system node pool before disabling
5217+
# the hosted system profile.
5218+
if not mc.agent_pool_profiles:
5219+
if mc.hosted_system_profile and mc.hosted_system_profile.enabled:
5220+
return mc
5221+
raise UnknownError(
5222+
"Encounter an unexpected error while getting agent pool profiles from the cluster in the process of "
5223+
"updating agentpool profile."
5224+
)
5225+
5226+
# Call preview agentpool decorator method instead of default
5227+
agentpool_profile = self.agentpool_decorator.update_agentpool_profile_preview(mc.agent_pool_profiles)
5228+
mc.agent_pool_profiles[0] = agentpool_profile
5229+
5230+
# Update nodepool labels for all nodepools
5231+
nodepool_labels = self.context.get_nodepool_labels()
5232+
if nodepool_labels is not None:
5233+
for agent_profile in mc.agent_pool_profiles:
5234+
agent_profile.node_labels = nodepool_labels
5235+
5236+
# Update nodepool taints for all nodepools
5237+
nodepool_taints = self.context.get_nodepool_taints()
5238+
if nodepool_taints is not None:
5239+
for agent_profile in mc.agent_pool_profiles:
5240+
agent_profile.node_taints = nodepool_taints
5241+
return mc
5242+
52045243
def update_network_profile(self, mc: ManagedCluster) -> ManagedCluster:
52055244
self._ensure_mc(mc)
52065245

@@ -6522,34 +6561,17 @@ def update_upgrade_settings(self, mc: ManagedCluster) -> ManagedCluster:
65226561

65236562
return mc
65246563

6525-
def update_nodepool_taints_mc(self, mc: ManagedCluster) -> ManagedCluster:
6526-
self._ensure_mc(mc)
6527-
6528-
if not mc.agent_pool_profiles:
6529-
raise UnknownError(
6530-
"Encounter an unexpected error while getting agent pool profiles from the cluster in the process of "
6531-
"updating agentpool profile."
6532-
)
6533-
6534-
# update nodepool taints for all nodepools
6535-
nodepool_taints = self.context.get_nodepool_taints()
6536-
if nodepool_taints is not None:
6537-
for agent_profile in mc.agent_pool_profiles:
6538-
agent_profile.node_taints = nodepool_taints
6539-
return mc
6540-
65416564
def update_nodepool_initialization_taints_mc(self, mc: ManagedCluster) -> ManagedCluster:
65426565
self._ensure_mc(mc)
65436566

6544-
if not mc.agent_pool_profiles:
6545-
raise UnknownError(
6546-
"Encounter an unexpected error while getting agent pool profiles from the cluster in the process of "
6547-
"updating agentpool profile."
6548-
)
6549-
65506567
# update nodepool taints for all nodepools
65516568
nodepool_initialization_taints = self.context.get_nodepool_initialization_taints()
65526569
if nodepool_initialization_taints is not None:
6570+
if not mc.agent_pool_profiles:
6571+
raise UnknownError(
6572+
"Encounter an unexpected error while getting agent pool profiles from the "
6573+
"cluster in the process of updating agentpool profile."
6574+
)
65536575
for agent_profile in mc.agent_pool_profiles:
65546576
if agent_profile.mode is not None and agent_profile.mode.lower() == "user":
65556577
agent_profile.node_initialization_taints = nodepool_initialization_taints
@@ -7245,8 +7267,6 @@ def update_mc_profile_preview(self) -> ManagedCluster:
72457267
mc = self.update_auto_upgrade_profile(mc)
72467268
# update cluster upgrade settings profile
72477269
mc = self.update_upgrade_settings(mc)
7248-
# update nodepool taints
7249-
mc = self.update_nodepool_taints_mc(mc)
72507270
# update nodepool initialization taints
72517271
mc = self.update_nodepool_initialization_taints_mc(mc)
72527272
# update acns in network_profile

src/aks-preview/azext_aks_preview/tests/latest/test_custom.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
)
1313
from azext_aks_preview.custom import (
1414
aks_stop,
15+
aks_scale,
16+
aks_upgrade,
17+
aks_enable_addons,
1518
)
1619
from azext_aks_preview.tests.latest.mocks import MockCLI, MockClient, MockCmd
1720

@@ -49,6 +52,87 @@ def test_aks_stop(self):
4952
)
5053
self.assertEqual(aks_stop(self.cmd, self.client, "rg", "name", False), None)
5154

55+
def test_aks_scale_with_none_agent_pool_profiles(self):
56+
"""Test aks_scale handles None agent_pool_profiles gracefully"""
57+
# Test case: automatic cluster with hosted system components, no agent pools
58+
mc = self.models.ManagedCluster(location="test_location")
59+
mc.agent_pool_profiles = None # This is the key scenario
60+
mc.pod_identity_profile = None
61+
62+
self.client.get = Mock(return_value=mc)
63+
64+
# Should not raise NoneType error and should return without crashing
65+
try:
66+
result = aks_scale(self.cmd, self.client, "rg", "name", 3, "nodepool1")
67+
# We expect this to complete without NoneType errors
68+
except Exception as e:
69+
# Should not be a NoneType error
70+
self.assertNotIn("NoneType", str(type(e)))
71+
72+
def test_aks_upgrade_with_none_agent_pool_profiles(self):
73+
"""Test aks_upgrade handles None agent_pool_profiles gracefully"""
74+
mc = self.models.ManagedCluster(location="test_location")
75+
mc.agent_pool_profiles = None # Key test scenario
76+
mc.pod_identity_profile = None
77+
mc.kubernetes_version = "1.24.0"
78+
mc.provisioning_state = "Succeeded"
79+
mc.max_agent_pools = 10
80+
81+
self.client.get = Mock(return_value=mc)
82+
83+
# Should not raise NoneType error
84+
try:
85+
result = aks_upgrade(
86+
self.cmd, self.client, "rg", "name",
87+
kubernetes_version="1.25.0", yes=True
88+
)
89+
except Exception as e:
90+
self.assertNotIn("NoneType", str(type(e)))
91+
92+
def test_aks_enable_addons_with_none_agent_pool_profiles(self):
93+
"""Test aks_enable_addons handles None agent_pool_profiles gracefully"""
94+
mc = self.models.ManagedCluster(location="test_location")
95+
mc.agent_pool_profiles = None # Key test scenario
96+
mc.addon_profiles = {}
97+
mc.service_principal_profile = self.models.ManagedClusterServicePrincipalProfile(
98+
client_id="msi"
99+
)
100+
mc.api_server_access_profile = None
101+
102+
self.client.get = Mock(return_value=mc)
103+
self.client.begin_create_or_update = Mock(return_value=mc)
104+
105+
# Should not raise NoneType error
106+
try:
107+
result = aks_enable_addons(
108+
self.cmd, self.client, "rg", "name", "monitoring",
109+
workspace_resource_id="/subscriptions/test/resourceGroups/test/providers/Microsoft.OperationalInsights/workspaces/test"
110+
)
111+
except Exception as e:
112+
self.assertNotIn("NoneType", str(type(e)))
113+
114+
def test_aks_enable_addons_virtual_node_with_none_agent_pool_profiles(self):
115+
"""Test aks_enable_addons for virtual-node handles None agent_pool_profiles"""
116+
mc = self.models.ManagedCluster(location="test_location")
117+
mc.agent_pool_profiles = None # Key test scenario for virtual node addon
118+
mc.addon_profiles = {}
119+
mc.service_principal_profile = self.models.ManagedClusterServicePrincipalProfile(
120+
client_id="msi"
121+
)
122+
mc.api_server_access_profile = None
123+
124+
self.client.get = Mock(return_value=mc)
125+
self.client.begin_create_or_update = Mock(return_value=mc)
126+
127+
# Virtual node addon should handle None agent_pool_profiles gracefully
128+
try:
129+
result = aks_enable_addons(
130+
self.cmd, self.client, "rg", "name", "virtual-node",
131+
subnet_name="test-subnet"
132+
)
133+
except Exception as e:
134+
self.assertNotIn("NoneType", str(type(e)))
135+
52136

53137
if __name__ == '__main__':
54138
unittest.main()

src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12903,5 +12903,97 @@ def test_azure_keyvault_kms_network_access_parameter_fix(self):
1290312903
"Public"
1290412904
)
1290512905

12906+
def test_update_agentpool_profile_with_none_agent_pool_profiles(self):
12907+
"""Test update_agentpool_profile handles None agent_pool_profiles with hosted system components"""
12908+
# Test case 1: None agent_pool_profiles with hosted system components enabled (should succeed)
12909+
dec_1 = AKSPreviewManagedClusterUpdateDecorator(
12910+
self.cmd,
12911+
self.client,
12912+
{},
12913+
CUSTOM_MGMT_AKS_PREVIEW,
12914+
)
12915+
12916+
# Create a managed cluster with None agent_pool_profiles but hosted system components enabled
12917+
hosted_system_profile = self.models.ManagedClusterHostedSystemProfile(enabled=True)
12918+
mc_1 = self.models.ManagedCluster(
12919+
location="test_location",
12920+
agent_pool_profiles=None, # This is the key scenario
12921+
hosted_system_profile=hosted_system_profile
12922+
)
12923+
dec_1.context.attach_mc(mc_1)
12924+
12925+
# Should return the MC unchanged without raising an error
12926+
result_1 = dec_1.update_agentpool_profile(mc_1)
12927+
self.assertEqual(result_1, mc_1)
12928+
self.assertIsNone(result_1.agent_pool_profiles)
12929+
self.assertTrue(result_1.hosted_system_profile.enabled)
12930+
12931+
def test_update_agentpool_profile_with_none_agent_pool_profiles_no_hosted_system(self):
12932+
"""Test update_agentpool_profile raises UnknownError for None agent_pool_profiles without hosted system components"""
12933+
# Test case 2: None agent_pool_profiles without hosted system components (should raise UnknownError)
12934+
dec_2 = AKSPreviewManagedClusterUpdateDecorator(
12935+
self.cmd,
12936+
self.client,
12937+
{},
12938+
CUSTOM_MGMT_AKS_PREVIEW,
12939+
)
12940+
12941+
# Create a managed cluster with None agent_pool_profiles and no hosted system components
12942+
mc_2 = self.models.ManagedCluster(
12943+
location="test_location",
12944+
agent_pool_profiles=None, # This is the key scenario
12945+
hosted_system_profile=None
12946+
)
12947+
dec_2.context.attach_mc(mc_2)
12948+
12949+
# Should raise UnknownError
12950+
with self.assertRaises(UnknownError):
12951+
dec_2.update_agentpool_profile(mc_2)
12952+
12953+
def test_update_agentpool_profile_with_none_agent_pool_profiles_hosted_system_disabled(self):
12954+
"""Test update_agentpool_profile raises UnknownError for None agent_pool_profiles with hosted system components disabled"""
12955+
# Test case 3: None agent_pool_profiles with hosted system components disabled (should raise UnknownError)
12956+
dec_3 = AKSPreviewManagedClusterUpdateDecorator(
12957+
self.cmd,
12958+
self.client,
12959+
{},
12960+
CUSTOM_MGMT_AKS_PREVIEW,
12961+
)
12962+
12963+
# Create a managed cluster with None agent_pool_profiles and hosted system components disabled
12964+
hosted_system_profile_disabled = self.models.ManagedClusterHostedSystemProfile(enabled=False)
12965+
mc_3 = self.models.ManagedCluster(
12966+
location="test_location",
12967+
agent_pool_profiles=None, # This is the key scenario
12968+
hosted_system_profile=hosted_system_profile_disabled
12969+
)
12970+
dec_3.context.attach_mc(mc_3)
12971+
12972+
# Should raise UnknownError
12973+
with self.assertRaises(UnknownError):
12974+
dec_3.update_agentpool_profile(mc_3)
12975+
12976+
def test_update_agentpool_profile_with_empty_agent_pool_profiles(self):
12977+
"""Test update_agentpool_profile raises UnknownError for empty agent_pool_profiles list"""
12978+
# Test case 4: Empty agent_pool_profiles list (should raise UnknownError)
12979+
dec_4 = AKSPreviewManagedClusterUpdateDecorator(
12980+
self.cmd,
12981+
self.client,
12982+
{},
12983+
CUSTOM_MGMT_AKS_PREVIEW,
12984+
)
12985+
12986+
# Create a managed cluster with empty agent_pool_profiles
12987+
mc_4 = self.models.ManagedCluster(
12988+
location="test_location",
12989+
agent_pool_profiles=[], # Empty list scenario
12990+
hosted_system_profile=None
12991+
)
12992+
dec_4.context.attach_mc(mc_4)
12993+
12994+
# Should raise UnknownError
12995+
with self.assertRaises(UnknownError):
12996+
dec_4.update_agentpool_profile(mc_4)
12997+
1290612998
if __name__ == "__main__":
1290712999
unittest.main()

src/aks-preview/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from setuptools import find_packages, setup
1111

12-
VERSION = "19.0.0b14"
12+
VERSION = "19.0.0b15"
1313

1414
CLASSIFIERS = [
1515
"Development Status :: 4 - Beta",

0 commit comments

Comments
 (0)