Skip to content

Commit ffd1ba5

Browse files
authored
feat: add disable_artifact_streaming arg for nodepool update in aks preview (#9679)
1 parent e296b27 commit ffd1ba5

10 files changed

Lines changed: 253 additions & 3 deletions

File tree

‎src/aks-preview/HISTORY.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Pending
2222
* `az aks list-vm-skus`: New command to list available VM SKUs for AKS clusters in a given region.
2323
* Add managed GPU enablement option to node pool property in `az aks nodepool add` and `az aks nodepool update`.
2424
* `az aks namespace update`: Fix location should use existing namespace location.
25+
* `az aks nodepool update`: Add `--disable-artifact-streaming` to disable artifact streaming.
2526

2627
19.0.0b27
2728
+++++++

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,9 @@
24372437
- name: --enable-artifact-streaming
24382438
type: bool
24392439
short-summary: Enable artifact streaming for VirtualMachineScaleSets managed by a node pool, to speed up the cold-start of containers on a node through on-demand image loading. To use this feature, container images must also enable artifact streaming on ACR. If not specified, the default is false.
2440+
- name: --disable-artifact-streaming
2441+
type: bool
2442+
short-summary: Disable artifact streaming for VirtualMachineScaleSets managed by a node pool.
24402443
- name: --enable-managed-gpu
24412444
type: bool
24422445
short-summary: Enable the Managed GPU experience, which installs additional components like DCGM metrics for monitoring on top of the GPU driver. For more details, visit aka.ms/aks/managed-gpu.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,6 +2227,12 @@ def load_arguments(self, _):
22272227
validator=validate_artifact_streaming,
22282228
is_preview=True,
22292229
)
2230+
c.argument(
2231+
"disable_artifact_streaming",
2232+
action="store_true",
2233+
validator=validate_artifact_streaming,
2234+
is_preview=True,
2235+
)
22302236
c.argument(
22312237
"enable_managed_gpu",
22322238
action="store_true",

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -954,10 +954,20 @@ def validate_asm_egress_name(namespace):
954954

955955

956956
def validate_artifact_streaming(namespace):
957-
"""Validates that artifact streaming enablement can only be used on Linux."""
958-
if namespace.enable_artifact_streaming:
959-
if hasattr(namespace, 'os_type') and str(namespace.os_type).lower() == "windows":
957+
"""Validates artifact streaming flags for mutual exclusivity and OS support."""
958+
enable_artifact_streaming = getattr(namespace, "enable_artifact_streaming", False)
959+
disable_artifact_streaming = getattr(namespace, "disable_artifact_streaming", False)
960+
961+
if enable_artifact_streaming and disable_artifact_streaming:
962+
raise MutuallyExclusiveArgumentError(
963+
"Cannot specify both --enable-artifact-streaming and --disable-artifact-streaming at the same time."
964+
)
965+
966+
if hasattr(namespace, "os_type") and str(namespace.os_type).lower() == "windows":
967+
if enable_artifact_streaming:
960968
raise ArgumentUsageError('--enable-artifact-streaming can only be set for Linux nodepools')
969+
if disable_artifact_streaming:
970+
raise ArgumentUsageError('--disable-artifact-streaming can only be set for Linux nodepools')
961971

962972

963973
def validate_custom_endpoints(namespace):

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,11 @@ def get_enable_artifact_streaming(self) -> bool:
588588
self.agentpool.artifact_streaming_profile.enabled is not None
589589
):
590590
enable_artifact_streaming = self.agentpool.artifact_streaming_profile.enabled
591+
592+
if enable_artifact_streaming and self.get_disable_artifact_streaming():
593+
raise MutuallyExclusiveArgumentError(
594+
'Cannot specify both --enable-artifact-streaming and --disable-artifact-streaming.'
595+
)
591596
return enable_artifact_streaming
592597

593598
def get_enable_managed_gpu(self) -> Union[bool, None]:
@@ -611,6 +616,13 @@ def get_enable_managed_gpu(self) -> Union[bool, None]:
611616
)
612617
return enable_managed_gpu
613618

619+
def get_disable_artifact_streaming(self) -> bool:
620+
"""Obtain the value of disable_artifact_streaming.
621+
:return: bool
622+
"""
623+
624+
return self.raw_param.get("disable_artifact_streaming")
625+
614626
def get_pod_ip_allocation_mode(self: bool = False) -> Union[str, None]:
615627
"""Get the value of pod_ip_allocation_mode.
616628
:return: str or None
@@ -1743,6 +1755,11 @@ def update_artifact_streaming(self, agentpool: AgentPool) -> AgentPool:
17431755
if agentpool.artifact_streaming_profile is None:
17441756
agentpool.artifact_streaming_profile = self.models.AgentPoolArtifactStreamingProfile() # pylint: disable=no-member
17451757
agentpool.artifact_streaming_profile.enabled = True
1758+
1759+
if self.context.get_disable_artifact_streaming():
1760+
if agentpool.artifact_streaming_profile is None:
1761+
agentpool.artifact_streaming_profile = self.models.AgentPoolArtifactStreamingProfile() # pylint: disable=no-member
1762+
agentpool.artifact_streaming_profile.enabled = False
17461763
return agentpool
17471764

17481765
def update_managed_gpu(self, agentpool: AgentPool) -> AgentPool:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,7 @@ def aks_agentpool_update(
20052005
allowed_host_ports=None,
20062006
asg_ids=None,
20072007
enable_artifact_streaming=False,
2008+
disable_artifact_streaming=False,
20082009
enable_managed_gpu=False,
20092010
os_sku=None,
20102011
ssh_access=None,

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,24 @@ def common_get_enable_artifact_streaming(self):
258258
ctx_2.attach_agentpool(agentpool_2)
259259
self.assertEqual(ctx_2.get_enable_artifact_streaming(), None)
260260

261+
def common_get_disable_artifact_streaming(self):
262+
# default
263+
ctx_1 = AKSPreviewAgentPoolContext(
264+
self.cmd,
265+
AKSAgentPoolParamDict({"disable_artifact_streaming": True}),
266+
self.models,
267+
DecoratorMode.UPDATE,
268+
self.agentpool_decorator_mode,
269+
)
270+
self.assertEqual(ctx_1.get_disable_artifact_streaming(), True)
271+
agentpool_1 = self.create_initialized_agentpool_instance(
272+
artifact_streaming_profile=self.models.AgentPoolArtifactStreamingProfile(
273+
enabled=True
274+
)
275+
)
276+
ctx_1.attach_agentpool(agentpool_1)
277+
self.assertEqual(ctx_1.get_disable_artifact_streaming(), True)
278+
261279
def common_get_enable_managed_gpu(self):
262280
# default
263281
ctx_1 = AKSPreviewAgentPoolContext(
@@ -1077,6 +1095,9 @@ def test_get_workload_runtime(self):
10771095
def test_get_enable_artifact_streaming(self):
10781096
self.common_get_enable_artifact_streaming()
10791097

1098+
def test_get_disable_artifact_streaming(self):
1099+
self.common_get_disable_artifact_streaming()
1100+
10801101
def test_get_enable_managed_gpu(self):
10811102
self.common_get_enable_managed_gpu()
10821103

@@ -1186,6 +1207,9 @@ def test_get_os_sku(self):
11861207
def test_get_enable_artifact_streaming(self):
11871208
self.common_get_enable_artifact_streaming()
11881209

1210+
def test_get_disable_artifact_streaming(self):
1211+
self.common_get_disable_artifact_streaming()
1212+
11891213
def test_get_enable_secure_boot(self):
11901214
self.common_get_enable_secure_boot()
11911215

@@ -2425,6 +2449,42 @@ def common_update_artifact_streaming(self):
24252449
)
24262450
)
24272451
self.assertEqual(dec_agentpool_2, ground_truth_agentpool_2)
2452+
2453+
dec_3 = AKSPreviewAgentPoolUpdateDecorator(
2454+
self.cmd,
2455+
self.client,
2456+
{"disable_artifact_streaming": True},
2457+
self.resource_type,
2458+
self.agentpool_decorator_mode,
2459+
)
2460+
# fail on passing the wrong agentpool object
2461+
with self.assertRaises(CLIInternalError):
2462+
dec_3.update_artifact_streaming(None)
2463+
agentpool_3 = self.create_initialized_agentpool_instance(
2464+
artifact_streaming_profile=self.models.AgentPoolArtifactStreamingProfile(
2465+
enabled=True
2466+
)
2467+
)
2468+
dec_3.context.attach_agentpool(agentpool_3)
2469+
dec_agentpool_3 = dec_3.update_artifact_streaming(agentpool_3)
2470+
grond_truth_agentpool_3 = self.create_initialized_agentpool_instance(
2471+
artifact_streaming_profile=self.models.AgentPoolArtifactStreamingProfile(
2472+
enabled=False
2473+
)
2474+
)
2475+
self.assertEqual(dec_agentpool_3, grond_truth_agentpool_3)
2476+
2477+
# Should error if both set
2478+
dec_4 = AKSPreviewAgentPoolUpdateDecorator(
2479+
self.cmd,
2480+
self.client,
2481+
{"enable_artifact_streaming": True, "disable_artifact_streaming": True},
2482+
self.resource_type,
2483+
self.agentpool_decorator_mode,
2484+
)
2485+
dec_4.context.attach_agentpool(agentpool_3)
2486+
with self.assertRaises(MutuallyExclusiveArgumentError):
2487+
dec_4.update_artifact_streaming(agentpool_3)
24282488

24292489
def common_update_managed_gpu(self):
24302490
dec_1 = AKSPreviewAgentPoolUpdateDecorator(

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16674,6 +16674,102 @@ def test_aks_nodepool_update_with_artifact_streaming(
1667416674
],
1667516675
)
1667616676

16677+
@live_only()
16678+
@AllowLargeResponse()
16679+
@AKSCustomResourceGroupPreparer(
16680+
random_name_length=17, name_prefix="clitest", location="eastus"
16681+
)
16682+
def test_aks_nodepool_update_with_disable_artifact_streaming(
16683+
self, resource_group, resource_group_location
16684+
):
16685+
aks_name = self.create_random_name("cliakstest", 16)
16686+
nodepool_name = self.create_random_name("n", 6)
16687+
16688+
self.kwargs.update(
16689+
{
16690+
"resource_group": resource_group,
16691+
"name": aks_name,
16692+
"location": resource_group_location,
16693+
"ssh_key_value": self.generate_ssh_keys(),
16694+
"node_pool_name": nodepool_name,
16695+
"node_vm_size": "standard_d2s_v3",
16696+
}
16697+
)
16698+
16699+
self.cmd(
16700+
"aks create "
16701+
"--resource-group={resource_group} "
16702+
"--name={name} "
16703+
"--location={location} "
16704+
"--ssh-key-value={ssh_key_value} "
16705+
"--nodepool-name={node_pool_name} "
16706+
"--node-count=1 "
16707+
"--node-vm-size={node_vm_size} "
16708+
"--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/ArtifactStreamingPreview",
16709+
checks=[
16710+
self.check("provisioningState", "Succeeded"),
16711+
],
16712+
)
16713+
16714+
# disable artifact streaming on a nodepool that never had it enabled
16715+
self.cmd(
16716+
"aks nodepool update "
16717+
"--resource-group={resource_group} "
16718+
"--cluster-name={name} "
16719+
"--name={node_pool_name} "
16720+
"--disable-artifact-streaming "
16721+
"--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/ArtifactStreamingPreview",
16722+
checks=[
16723+
self.check("provisioningState", "Succeeded"),
16724+
self.check(
16725+
"artifactStreamingProfile.enabled", False
16726+
),
16727+
],
16728+
)
16729+
16730+
# enable artifact streaming
16731+
self.cmd(
16732+
"aks nodepool update "
16733+
"--resource-group={resource_group} "
16734+
"--cluster-name={name} "
16735+
"--name={node_pool_name} "
16736+
"--enable-artifact-streaming "
16737+
"--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/ArtifactStreamingPreview",
16738+
checks=[
16739+
self.check("provisioningState", "Succeeded"),
16740+
self.check(
16741+
"artifactStreamingProfile.enabled", True
16742+
),
16743+
],
16744+
)
16745+
16746+
# disable artifact streaming
16747+
self.cmd(
16748+
"aks nodepool update "
16749+
"--resource-group={resource_group} "
16750+
"--cluster-name={name} "
16751+
"--name={node_pool_name} "
16752+
"--disable-artifact-streaming "
16753+
"--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/ArtifactStreamingPreview",
16754+
checks=[
16755+
self.check("provisioningState", "Succeeded"),
16756+
self.check(
16757+
"artifactStreamingProfile.enabled", False
16758+
),
16759+
],
16760+
)
16761+
16762+
# delete
16763+
cmd = (
16764+
"aks delete --resource-group={resource_group} --name={name} --yes --no-wait"
16765+
)
16766+
self.cmd(
16767+
cmd,
16768+
checks=[
16769+
self.is_empty(),
16770+
],
16771+
)
16772+
1667716773
@live_only()
1667816774
@AllowLargeResponse()
1667916775
@AKSCustomResourceGroupPreparer(

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ def __init__(self, os_type, disable_windows_outbound_nat):
149149
self.disable_windows_outbound_nat = disable_windows_outbound_nat
150150

151151

152+
class ArtifactStreamingNamespace:
153+
def __init__(self, os_type, enable_artifact_streaming=False, disable_artifact_streaming=False):
154+
self.os_type = os_type
155+
self.enable_artifact_streaming = enable_artifact_streaming
156+
self.disable_artifact_streaming = disable_artifact_streaming
157+
158+
152159
class TestMaxSurge(unittest.TestCase):
153160
def test_valid_cases(self):
154161
valid = ["5", "33%", "1", "100%"]
@@ -385,6 +392,52 @@ def test_fail_if_os_type_invalid(self):
385392
)
386393

387394

395+
class TestArtifactStreaming(unittest.TestCase):
396+
def test_valid_linux_enable(self):
397+
validators.validate_artifact_streaming(
398+
ArtifactStreamingNamespace("Linux", enable_artifact_streaming=True)
399+
)
400+
401+
def test_valid_linux_disable(self):
402+
validators.validate_artifact_streaming(
403+
ArtifactStreamingNamespace("Linux", disable_artifact_streaming=True)
404+
)
405+
406+
def test_fail_if_enable_and_disable_are_set(self):
407+
with self.assertRaises(MutuallyExclusiveArgumentError) as cm:
408+
validators.validate_artifact_streaming(
409+
ArtifactStreamingNamespace(
410+
"Linux",
411+
enable_artifact_streaming=True,
412+
disable_artifact_streaming=True,
413+
)
414+
)
415+
self.assertEqual(
416+
str(cm.exception),
417+
"Cannot specify both --enable-artifact-streaming and --disable-artifact-streaming at the same time.",
418+
)
419+
420+
def test_fail_if_enable_for_windows(self):
421+
with self.assertRaises(ArgumentUsageError) as cm:
422+
validators.validate_artifact_streaming(
423+
ArtifactStreamingNamespace("Windows", enable_artifact_streaming=True)
424+
)
425+
self.assertEqual(
426+
str(cm.exception),
427+
"--enable-artifact-streaming can only be set for Linux nodepools",
428+
)
429+
430+
def test_fail_if_disable_for_windows(self):
431+
with self.assertRaises(ArgumentUsageError) as cm:
432+
validators.validate_artifact_streaming(
433+
ArtifactStreamingNamespace("Windows", disable_artifact_streaming=True)
434+
)
435+
self.assertEqual(
436+
str(cm.exception),
437+
"--disable-artifact-streaming can only be set for Linux nodepools",
438+
)
439+
440+
388441
class ValidateAddonsNamespace:
389442
def __init__(self, addons):
390443
self.addons = addons

‎src/aks-preview/linter_exclusions.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ aks nodepool update:
446446
enable_artifact_streaming:
447447
rule_exclusions:
448448
- option_length_too_long
449+
disable_artifact_streaming:
450+
rule_exclusions:
451+
- option_length_too_long
449452
enable_secure_boot:
450453
rule_exclusions:
451454
- option_length_too_long

0 commit comments

Comments
 (0)