diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index eedb4f39f93..864b4262ab0 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,6 +12,7 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ * `az aks create` and `az aks nodepool add`: Add `--enable-osdisk-full-caching` (preview) to enable the full-cache ephemeral OS disk feature for a node pool. Requires AFEC registration `Microsoft.ContainerService/FullCachePreview`. Property is immutable after node pool creation. +* `az aks update`: Add `--node-disruption-policy` (preview) to update the node disruption policy for a cluster. Requires AFEC registration `Microsoft.ContainerService/NodeDisruptionProfile`. This is a cluster-level property that applies to all node pools in the cluster. 21.0.0b1 ++++++ diff --git a/src/aks-preview/azext_aks_preview/_consts.py b/src/aks-preview/azext_aks_preview/_consts.py index 90408f8a294..9b278d768ff 100644 --- a/src/aks-preview/azext_aks_preview/_consts.py +++ b/src/aks-preview/azext_aks_preview/_consts.py @@ -418,3 +418,8 @@ CONST_K8S_EXTENSION_NAME = "k8s-extension" CONST_K8S_EXTENSION_ACTION_MOD_NAME = "azext_k8s_extension.action" CONST_K8S_EXTENSION_FORMAT_MOD_NAME = "azext_k8s_extension._format" + +# Node Disruption Policy Consts +CONST_NODE_DISRUPTION_POLICY_ALLOW = "Allow" +CONST_NODE_DISRUPTION_POLICY_BLOCK = "Block" +CONST_NODE_DISRUPTION_POLICY_ALLOW_DURING_MAINTENANCE_WINDOW = "AllowDuringMaintenanceWindow" diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index f4deb15bca9..342adf1e14a 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -78,6 +78,9 @@ CONST_NETWORK_PLUGIN_NONE, CONST_NETWORK_POD_IP_ALLOCATION_MODE_DYNAMIC_INDIVIDUAL, CONST_NETWORK_POD_IP_ALLOCATION_MODE_STATIC_BLOCK, + CONST_NODE_DISRUPTION_POLICY_ALLOW, + CONST_NODE_DISRUPTION_POLICY_BLOCK, + CONST_NODE_DISRUPTION_POLICY_ALLOW_DURING_MAINTENANCE_WINDOW, CONST_NODE_IMAGE_UPGRADE_CHANNEL, CONST_NODE_OS_CHANNEL_NODE_IMAGE, CONST_NODE_OS_CHANNEL_NONE, @@ -561,6 +564,12 @@ CONST_UPGRADE_STRATEGY_BLUE_GREEN, ] +node_disruption_policies = [ + CONST_NODE_DISRUPTION_POLICY_ALLOW, + CONST_NODE_DISRUPTION_POLICY_BLOCK, + CONST_NODE_DISRUPTION_POLICY_ALLOW_DURING_MAINTENANCE_WINDOW, +] + def load_arguments(self, _): acr_arg_type = CLIArgumentType(metavar="ACR_NAME_OR_RESOURCE_ID") @@ -1918,6 +1927,12 @@ def load_arguments(self, _): is_preview=True, help="Disable continuous control plane and addon monitor for the cluster.", ) + c.argument( + "node_disruption_policy", + arg_type=get_enum_type(node_disruption_policies), + is_preview=True, + help="Set the node disruption policy for 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 77ab483c1ad..bf79e841f26 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -1439,6 +1439,8 @@ def aks_update( # health monitor enable_continuous_control_plane_and_addon_monitor=False, disable_continuous_control_plane_and_addon_monitor=False, + # node disruption policy + node_disruption_policy=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 60f22700aaa..c6e037b9a36 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -3763,6 +3763,11 @@ def get_node_provisioning_mode(self) -> Union[str, None]: """ return self.raw_param.get("node_provisioning_mode") + def get_node_disruption_policy(self) -> Union[str, None]: + """Obtain the value of node_disruption_policy. + """ + return self.raw_param.get("node_disruption_policy") + def get_node_provisioning_default_pools(self) -> Union[str, None]: """Obtain the value of node_provisioning_default_pools. """ @@ -7629,6 +7634,25 @@ def update_node_provisioning_profile(self, mc: ManagedCluster) -> ManagedCluster return mc + def update_node_disruption_policy(self, mc: ManagedCluster) -> ManagedCluster: + """Updates the nodeDisruptionPolicy field of the managed cluster + + :return: the ManagedCluster object + """ + self._ensure_mc(mc) + + policy = self.context.get_node_disruption_policy() + if policy is not None: + if mc.node_disruption_profile is None: + mc.node_disruption_profile = ( + self.models.NodeDisruptionProfile() # pylint: disable=no-member + ) + + # set policy + mc.node_disruption_profile.policy = policy + + return mc + def update_ai_toolchain_operator(self, mc: ManagedCluster) -> ManagedCluster: """Updates the aiToolchainOperatorProfile field of the managed cluster @@ -8166,6 +8190,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 node disruption policy + mc = self.update_node_disruption_policy(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 99fd082c123..0e08f0e62a8 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 @@ -24248,3 +24248,79 @@ def test_aks_nodepool_update_vmss_vm_size_resize( self.check("vmSize", "Standard_D4s_v3"), ], ) + + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer( + random_name_length=17, name_prefix="clitest", location="westus2" + ) + def test_aks_update_node_disruption_policy(self, resource_group, resource_group_location): + aks_name = self.create_random_name("cliakstest", 16) + nodepool_name = self.create_random_name("c", 6) + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "location": resource_group_location, + "ssh_key_value": self.generate_ssh_keys(), + "resource_type": "Microsoft.ContainerService/ManagedClusters", + "nodepool_name": nodepool_name, + "vm_size": "Standard_D4s_v3", + "network_plugin": "azure", + "network_plugin_mode": "overlay", + } + ) + + # create aks cluster with Azure network plugin + self.cmd( + "aks create " + "--resource-group={resource_group} " + "--name={name} " + "--ssh-key-value={ssh_key_value} " + "--network-plugin={network_plugin} " + "--network-plugin-mode={network_plugin_mode} " + "--node-count=3", + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # add nodepool + self.cmd( + "aks nodepool add " + "--resource-group={resource_group}" + " --cluster-name={name} " + "--name={nodepool_name}", + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # update node disruption policy to "Block" + self.cmd( + "aks update --resource-group={resource_group} --name={name} --node-disruption-policy Block", + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # attempt to change network policy which should be blocked by node disruption policy + self.cmd( + "aks update --resource-group={resource_group} --name={name} --network-policy azure", + checks=[ + self.check("provisioningState", "Failed"), + ], + ) + + # attempt to change ssh access which should be allowed despite node disruption policy + self.cmd( + "aks nodepool update --resource-group={resource_group} --cluster-name={name} --name={nodepool_name} --ssh-access disabled", + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # delete + self.cmd( + "aks delete -g {resource_group} -n {name} --yes --no-wait", + checks=[self.is_empty()], + )