From 4eb5ae003c654babc8da54d16c0a75850d20b377 Mon Sep 17 00:00:00 2001 From: Neth Botheju Date: Thu, 30 Apr 2026 16:05:21 +1000 Subject: [PATCH] Add --control-plane-scaling-size parameter to az aks update Add support for updating the control plane scaling size on existing clusters via az aks update --control-plane-scaling-size. Users can upgrade or downgrade between H2, H4, and H8 sizes on clusters that were created with a control plane scaling profile. Server-side RP handles all validation. --- src/aks-preview/HISTORY.rst | 1 + src/aks-preview/azext_aks_preview/_params.py | 9 +++ src/aks-preview/azext_aks_preview/custom.py | 2 + .../managed_cluster_decorator.py | 22 ++++++ .../tests/latest/test_aks_commands.py | 26 +++++++ .../latest/test_managed_cluster_decorator.py | 75 +++++++++++++++++++ 6 files changed, 135 insertions(+) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 76f9e0f5030..329e3b77f6a 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -11,6 +11,7 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +* `az aks update`: Add `--control-plane-scaling-size` parameter to update the control plane scaling size on an existing cluster with available sizes 'H2', 'H4', and 'H8'. 20.0.0b5 +++++++ diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index b78493e8286..da866e7e1c1 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -1915,6 +1915,15 @@ def load_arguments(self, _): is_preview=True, help="Disable continuous control plane and addon monitor for the cluster.", ) + c.argument( + "control_plane_scaling_size", + options_list=["--control-plane-scaling-size", "--cp-scaling-size"], + arg_type=get_enum_type(["H2", "H4", "H8"]), + is_preview=True, + help="The control plane scaling size. Provides scaled and performance-guaranteed control plane capacity. " + "Available values are 'H2', 'H4', and 'H8'. " + "The control plane scaling profile must already be enabled on the cluster.", + ) with self.argument_context("aks delete") as c: c.argument("if_match") diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index 59422a1fb1c..196e64fb170 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -1438,6 +1438,8 @@ def aks_update( # health monitor enable_continuous_control_plane_and_addon_monitor=False, disable_continuous_control_plane_and_addon_monitor=False, + # control plane scaling + control_plane_scaling_size=None, ): # DO NOT MOVE: get all the original parameters and save them as a dictionary raw_parameters = locals() diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index 17ac59ea25f..12359e1f7e1 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -8087,6 +8087,26 @@ def update_addon_profiles(self, mc: ManagedCluster) -> ManagedCluster: return mc + def update_control_plane_scaling_profile(self, mc: ManagedCluster) -> ManagedCluster: + """Update the control plane scaling profile for the ManagedCluster object. + + :return: the ManagedCluster object + """ + self._ensure_mc(mc) + + control_plane_scaling_size = self.context.get_control_plane_scaling_size() + if control_plane_scaling_size is not None: + if mc.control_plane_scaling_profile is None: + mc.control_plane_scaling_profile = ( + self.models.ManagedClusterControlPlaneScalingProfile( # pylint: disable=no-member + scaling_size=control_plane_scaling_size, + ) + ) + else: + mc.control_plane_scaling_profile.scaling_size = control_plane_scaling_size + + return mc + def update_mc_profile_preview(self) -> ManagedCluster: """The overall controller used to update the preview ManagedCluster profile. @@ -8181,6 +8201,8 @@ def update_mc_profile_preview(self) -> ManagedCluster: mc = self.update_http_proxy_enabled(mc) # update user-defined scheduler configuration for kube-scheduler upstream mc = self.update_upstream_kubescheduler_user_configuration(mc) + # update control plane scaling profile + mc = self.update_control_plane_scaling_profile(mc) # update ManagedSystem pools, must at end mc = self.update_managed_system_pools(mc) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 649cdb97dfa..a2c921c0d71 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -10000,6 +10000,32 @@ def test_aks_create_with_control_plane_scaling_profile( ], ) + # update control plane scaling size from H4 to H8 + update_cmd = ( + "aks update --resource-group={resource_group} --name={name} " + "--control-plane-scaling-size H8" + ) + self.cmd( + update_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + self.check("controlPlaneScalingProfile.scalingSize", "H8"), + ], + ) + + # update control plane scaling size from H8 to H2 (downgrade) + update_cmd_2 = ( + "aks update --resource-group={resource_group} --name={name} " + "--control-plane-scaling-size H2" + ) + self.cmd( + update_cmd_2, + checks=[ + self.check("provisioningState", "Succeeded"), + self.check("controlPlaneScalingProfile.scalingSize", "H2"), + ], + ) + # delete self.cmd( "aks delete -g {resource_group} -n {name} --yes --no-wait", diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py index 39f53f81e3e..86dbe099e6e 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py @@ -16039,6 +16039,81 @@ def test_update_upstream_kubescheduler_user_configuration(self): ) self.assertEqual(dec_mc_5, ground_truth_mc_5) + def test_update_control_plane_scaling_profile(self): + # Test default behavior - no change when not specified + dec_0 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + {}, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_0 = self.models.ManagedCluster(location="test_location") + dec_0.context.attach_mc(mc_0) + dec_mc_0 = dec_0.update_control_plane_scaling_profile(mc_0) + self.assertIsNone(dec_mc_0.control_plane_scaling_profile) + + # Test updating scaling size on a cluster with existing profile (upgrade H4 -> H8) + from azext_aks_preview.vendored_sdks.azure_mgmt_preview_aks.models import ( + ManagedClusterControlPlaneScalingProfile, + ) + + dec_1 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "control_plane_scaling_size": "H8", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_1 = self.models.ManagedCluster( + location="test_location", + control_plane_scaling_profile=ManagedClusterControlPlaneScalingProfile( + scaling_size="H4", + ), + ) + dec_1.context.attach_mc(mc_1) + dec_mc_1 = dec_1.update_control_plane_scaling_profile(mc_1) + self.assertEqual(dec_mc_1.control_plane_scaling_profile.scaling_size, "H8") + + # Test updating scaling size on a cluster with existing profile (downgrade H8 -> H2) + dec_2 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "control_plane_scaling_size": "H2", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_2 = self.models.ManagedCluster( + location="test_location", + control_plane_scaling_profile=ManagedClusterControlPlaneScalingProfile( + scaling_size="H8", + ), + ) + dec_2.context.attach_mc(mc_2) + dec_mc_2 = dec_2.update_control_plane_scaling_profile(mc_2) + self.assertEqual(dec_mc_2.control_plane_scaling_profile.scaling_size, "H2") + + # Test setting scaling size when profile doesn't exist (passthrough to RP for validation) + dec_3 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "control_plane_scaling_size": "H4", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_3 = self.models.ManagedCluster(location="test_location") + dec_3.context.attach_mc(mc_3) + dec_mc_3 = dec_3.update_control_plane_scaling_profile(mc_3) + ground_truth_mc_3 = self.models.ManagedCluster( + location="test_location", + control_plane_scaling_profile=ManagedClusterControlPlaneScalingProfile( + scaling_size="H4", + ), + ) + self.assertEqual(dec_mc_3, ground_truth_mc_3) + def test_update_ingress_profile_gateway_api(self): # Test enabling Gateway API dec_1 = AKSPreviewManagedClusterUpdateDecorator(