Skip to content

Commit 8776fc5

Browse files
Fix Managed Lustre review findings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 30db9f4 commit 8776fc5

5 files changed

Lines changed: 158 additions & 9 deletions

File tree

‎src/aks-preview/README.rst‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ For an existing AKS cluster, enable or disable the extension with::
2929

3030
Enabling installs ``microsoft.azuremanagedlustre`` as ``azurelustre`` with cluster scope,
3131
version ``0.6.0``, release train ``stable``, and automatic upgrades disabled.
32+
Flatcar, Ubuntu 26.04, and Azure Linux 3 on ARM64 node pools are not supported
33+
by this extension version. The cluster must have at least one compatible node pool.
34+
Enabling an existing installation leaves its configuration unchanged.
3235
As with Azure Container Storage, these operations wait for cluster provisioning before
3336
installing or uninstalling the extension, even when ``--no-wait`` is specified.
3437
The enable and disable flags cannot be used together. Disabling uninstalls the cluster

‎src/aks-preview/azext_aks_preview/azuremanagedlustre/_consts.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77
CONST_AML_K8S_EXTENSION_NAME = "microsoft.azuremanagedlustre"
88
CONST_AML_VERSION = "0.6.0"
99
CONST_AML_RELEASE_TRAIN = "stable"
10+
CONST_AML_UNSUPPORTED_OS_SKUS = frozenset(("flatcar", "ubuntu2604"))
11+
CONST_AML_ARM64_UNSUPPORTED_OS_SKUS = frozenset(("azurelinux3",))

‎src/aks-preview/azext_aks_preview/azuremanagedlustre/_validators.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55

66
from azure.cli.core.azclierror import InvalidArgumentValueError, MutuallyExclusiveArgumentError
77

8+
from azext_aks_preview.azuremanagedlustre._consts import (
9+
CONST_AML_ARM64_UNSUPPORTED_OS_SKUS,
10+
CONST_AML_UNSUPPORTED_OS_SKUS,
11+
)
12+
813

914
def validate_azure_managed_lustre_params(enable, disable, is_extension_installed=None):
1015
if enable and disable:
@@ -15,3 +20,56 @@ def validate_azure_managed_lustre_params(enable, disable, is_extension_installed
1520
raise InvalidArgumentValueError(
1621
"Cannot set --disable-azure-managed-lustre. Azure Managed Lustre is not enabled in the cluster."
1722
)
23+
24+
25+
def _get_vm_sku_architectures(cli_ctx, location):
26+
from azure.cli.command_modules.vm.aaz.latest.vm import ListSkus as VMListSkus
27+
28+
def is_in_location(sku):
29+
return not location or any(location.lower() == value.lower() for value in sku.get("locations", []))
30+
31+
architectures = {}
32+
for sku in VMListSkus(cli_ctx=cli_ctx)(command_args={}):
33+
if sku.get("resourceType", "").lower() != "virtualmachines" or not is_in_location(sku):
34+
continue
35+
for capability in sku.get("capabilities", []):
36+
if capability.get("name", "").lower() == "cpuarchitecturetype":
37+
architectures[sku.get("name", "").lower()] = capability.get("value", "").lower()
38+
break
39+
return architectures
40+
41+
42+
def validate_azure_managed_lustre_node_compatibility(cmd, cluster):
43+
pools = cluster.agent_pool_profiles or []
44+
architecture_sensitive_pools = []
45+
46+
for pool in pools:
47+
os_sku = (pool.os_sku or "").lower()
48+
if os_sku in CONST_AML_UNSUPPORTED_OS_SKUS:
49+
continue
50+
if os_sku in CONST_AML_ARM64_UNSUPPORTED_OS_SKUS:
51+
architecture_sensitive_pools.append(pool)
52+
continue
53+
return
54+
55+
if architecture_sensitive_pools:
56+
architectures = _get_vm_sku_architectures(cmd.cli_ctx, cluster.location)
57+
unknown_vm_sizes = []
58+
for pool in architecture_sensitive_pools:
59+
vm_size = (pool.vm_size or "").lower()
60+
architecture = architectures.get(vm_size)
61+
if architecture and architecture != "arm64":
62+
return
63+
if not architecture:
64+
unknown_vm_sizes.append(pool.vm_size or "<unknown>")
65+
if unknown_vm_sizes:
66+
raise InvalidArgumentValueError(
67+
"Cannot enable Azure Managed Lustre because the architecture couldn't be determined for "
68+
f"node pool VM size(s): {', '.join(unknown_vm_sizes)}."
69+
)
70+
71+
raise InvalidArgumentValueError(
72+
"Cannot enable Azure Managed Lustre because none of the cluster's node pools support "
73+
"Azure Managed Lustre extension version 0.6.0. Flatcar, Ubuntu 26.04, and Azure Linux 3 "
74+
"on ARM64 node pools aren't supported."
75+
)

‎src/aks-preview/azext_aks_preview/managed_cluster_decorator.py‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@
104104
check_if_extension_is_installed as check_if_azure_managed_lustre_is_installed,
105105
get_azure_managed_lustre_extension_client,
106106
)
107-
from azext_aks_preview.azuremanagedlustre._validators import validate_azure_managed_lustre_params
107+
from azext_aks_preview.azuremanagedlustre._validators import (
108+
validate_azure_managed_lustre_node_compatibility,
109+
validate_azure_managed_lustre_params,
110+
)
108111
from azext_aks_preview.azuremanagedlustre.aml_ops import (
109112
perform_disable_azure_managed_lustre,
110113
perform_enable_azure_managed_lustre,
@@ -5382,6 +5385,7 @@ def set_up_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster:
53825385
def set_up_azure_managed_lustre(self, mc: ManagedCluster) -> ManagedCluster:
53835386
self._ensure_mc(mc)
53845387
if self.context.raw_param.get("enable_azure_managed_lustre"):
5388+
validate_azure_managed_lustre_node_compatibility(self.cmd, mc)
53855389
get_azure_managed_lustre_extension_client(self.cmd)
53865390
self.context.set_intermediate("enable_azure_managed_lustre", True, overwrite_exists=True)
53875391
return mc
@@ -6292,6 +6296,11 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
62926296
True
62936297
)
62946298

6299+
if self.context.get_intermediate("enable_azure_managed_lustre", default_value=False):
6300+
self.context.external_functions.perform_enable_azure_managed_lustre(
6301+
self.cmd, self.context.get_resource_group_name(), self.context.get_name()
6302+
)
6303+
62956304
# enable azure container storage
62966305
enable_azure_container_storage = self.context.get_intermediate("enable_azure_container_storage")
62976306
container_storage_version = self.context.get_intermediate("container_storage_version")
@@ -6379,11 +6388,6 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
63796388
is_called_from_extension=True,
63806389
)
63816390

6382-
if self.context.get_intermediate("enable_azure_managed_lustre", default_value=False):
6383-
self.context.external_functions.perform_enable_azure_managed_lustre(
6384-
self.cmd, self.context.get_resource_group_name(), self.context.get_name()
6385-
)
6386-
63876391
# Add role assignments for automatic sku
63886392
if cluster.sku is not None and cluster.sku.name == "Automatic":
63896393
try:
@@ -6824,6 +6828,11 @@ def update_azure_managed_lustre(self, mc: ManagedCluster) -> ManagedCluster:
68246828
self.cmd, self.context.get_resource_group_name(), self.context.get_name()
68256829
)
68266830
validate_azure_managed_lustre_params(enable, disable, installed)
6831+
if enable and installed:
6832+
logger.warning("Azure Managed Lustre is already enabled. The existing configuration was preserved.")
6833+
enable = False
6834+
elif enable:
6835+
validate_azure_managed_lustre_node_compatibility(self.cmd, mc)
68276836
self.context.set_intermediate("enable_azure_managed_lustre", enable, overwrite_exists=True)
68286837
self.context.set_intermediate("disable_azure_managed_lustre", disable, overwrite_exists=True)
68296838
return mc

‎src/aks-preview/azext_aks_preview/tests/latest/test_azure_managed_lustre.py‎

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import inspect
77
import unittest
88
from types import SimpleNamespace
9-
from unittest.mock import Mock, patch
9+
from unittest.mock import Mock, call, patch
1010

1111
from azure.core.exceptions import HttpResponseError
1212
from azure.cli.core.parser import AzCliCommandParser
@@ -22,7 +22,10 @@
2222
check_if_extension_is_installed,
2323
get_azure_managed_lustre_extension_client,
2424
)
25-
from azext_aks_preview.azuremanagedlustre._validators import validate_azure_managed_lustre_params
25+
from azext_aks_preview.azuremanagedlustre._validators import (
26+
validate_azure_managed_lustre_node_compatibility,
27+
validate_azure_managed_lustre_params,
28+
)
2629
from azext_aks_preview.azuremanagedlustre.aml_ops import (
2730
perform_disable_azure_managed_lustre,
2831
perform_enable_azure_managed_lustre,
@@ -107,6 +110,53 @@ def test_valid_parameters(self):
107110
with self.subTest(enable=enable, disable=disable, installed=installed):
108111
validate_azure_managed_lustre_params(enable, disable, installed)
109112

113+
def test_supported_node_pool(self):
114+
cluster = SimpleNamespace(
115+
location="eastus",
116+
agent_pool_profiles=[SimpleNamespace(os_sku="Ubuntu2404", vm_size="Standard_D2s_v5")],
117+
)
118+
validate_azure_managed_lustre_node_compatibility(Mock(), cluster)
119+
120+
def test_unsupported_node_pool_os_skus(self):
121+
for os_sku in ["Flatcar", "Ubuntu2604"]:
122+
with self.subTest(os_sku=os_sku):
123+
cluster = SimpleNamespace(
124+
location="eastus",
125+
agent_pool_profiles=[SimpleNamespace(os_sku=os_sku, vm_size="Standard_D2s_v5")],
126+
)
127+
with self.assertRaisesRegex(InvalidArgumentValueError, "none of the cluster's node pools"):
128+
validate_azure_managed_lustre_node_compatibility(Mock(), cluster)
129+
130+
def test_azure_linux_3_node_pool_architecture(self):
131+
cluster = SimpleNamespace(
132+
location="eastus",
133+
agent_pool_profiles=[SimpleNamespace(os_sku="AzureLinux3", vm_size="Standard_D2ps_v5")],
134+
)
135+
with patch(
136+
"azext_aks_preview.azuremanagedlustre._validators._get_vm_sku_architectures",
137+
return_value={"standard_d2ps_v5": "arm64"},
138+
):
139+
with self.assertRaisesRegex(InvalidArgumentValueError, "ARM64"):
140+
validate_azure_managed_lustre_node_compatibility(Mock(), cluster)
141+
142+
with patch(
143+
"azext_aks_preview.azuremanagedlustre._validators._get_vm_sku_architectures",
144+
return_value={"standard_d2ps_v5": "x64"},
145+
):
146+
validate_azure_managed_lustre_node_compatibility(Mock(), cluster)
147+
148+
def test_unknown_azure_linux_3_node_pool_architecture(self):
149+
cluster = SimpleNamespace(
150+
location="eastus",
151+
agent_pool_profiles=[SimpleNamespace(os_sku="AzureLinux3", vm_size="Standard_Unknown")],
152+
)
153+
with patch(
154+
"azext_aks_preview.azuremanagedlustre._validators._get_vm_sku_architectures",
155+
return_value={},
156+
):
157+
with self.assertRaisesRegex(InvalidArgumentValueError, "architecture couldn't be determined"):
158+
validate_azure_managed_lustre_node_compatibility(Mock(), cluster)
159+
110160

111161
class AzureManagedLustreOperationsTestCase(unittest.TestCase):
112162
def setUp(self):
@@ -186,6 +236,11 @@ def make_decorator(self, create, **params):
186236
raw_params.update(params)
187237
decorator = decorator_type(self.cmd, self.client, raw_params, CUSTOM_MGMT_AKS_PREVIEW)
188238
decorator.context.attach_mc(self.models.ManagedCluster(location="eastus"))
239+
decorator.context.mc.agent_pool_profiles = [
240+
self.models.ManagedClusterAgentPoolProfile(
241+
name="nodepool1", os_sku="Ubuntu2404", vm_size="Standard_D2s_v5"
242+
)
243+
]
189244
decorator.context.set_intermediate("subscription_id", "test-subscription")
190245
return decorator
191246

@@ -282,7 +337,7 @@ def test_update_propagates_lookup_errors_before_cluster_request(self):
282337
self.client.begin_create_or_update.assert_not_called()
283338

284339
def test_update_prepares_enable_and_disable(self):
285-
for enable, installed in [(True, False), (True, True), (False, True)]:
340+
for enable, installed in [(True, False), (False, True)]:
286341
with self.subTest(enable=enable, installed=installed):
287342
decorator = self.make_decorator(
288343
False, enable_azure_managed_lustre=enable, disable_azure_managed_lustre=not enable
@@ -296,6 +351,16 @@ def test_update_prepares_enable_and_disable(self):
296351
self.assertEqual(decorator.context.get_intermediate("disable_azure_managed_lustre"), not enable)
297352
self.assertTrue(decorator.check_is_postprocessing_required(mc))
298353

354+
def test_update_preserves_existing_extension_configuration(self):
355+
decorator = self.make_decorator(False, enable_azure_managed_lustre=True)
356+
mc = decorator.context.mc
357+
with patch(DECORATOR + ".check_if_azure_managed_lustre_is_installed", return_value=True), \
358+
patch(DECORATOR + ".validate_azure_managed_lustre_node_compatibility") as validate:
359+
self.assertIs(decorator.update_azure_managed_lustre(mc), mc)
360+
validate.assert_not_called()
361+
self.assertFalse(decorator.context.get_intermediate("enable_azure_managed_lustre"))
362+
self.assertFalse(decorator.check_is_postprocessing_required(mc))
363+
299364
def test_postprocessing_performs_requested_operation(self):
300365
for create, enable in [(True, True), (False, True), (False, False)]:
301366
with self.subTest(create=create, enable=enable):
@@ -311,6 +376,18 @@ def test_postprocessing_performs_requested_operation(self):
311376
(install if enable else uninstall).assert_called_once_with(self.cmd, "rg", "cluster")
312377
(uninstall if enable else install).assert_not_called()
313378

379+
def test_create_installs_lustre_before_container_storage_early_return(self):
380+
decorator = self.make_decorator(True)
381+
decorator.context.set_intermediate("enable_azure_managed_lustre", True)
382+
decorator.context.set_intermediate("enable_azure_container_storage", "ephemeralDisk")
383+
decorator.context.set_intermediate("container_storage_version", "1")
384+
functions = decorator.context.external_functions
385+
with patch.object(functions, "perform_enable_azure_managed_lustre") as install, \
386+
patch("azure.cli.command_modules.acs.managed_cluster_decorator."
387+
"AKSManagedClusterUpdateDecorator.postprocessing_after_mc_created"):
388+
decorator.postprocessing_after_mc_created(decorator.context.mc)
389+
install.assert_called_once_with(self.cmd, "rg", "cluster")
390+
314391
def test_put_waits_for_cluster_before_lustre_even_with_no_wait(self):
315392
for create in [True, False]:
316393
with self.subTest(create=create):

0 commit comments

Comments
 (0)