diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index acf1efbf0c2..7416d3428ec 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,6 +12,10 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +22.0.0b3 ++++++++++ +* `az aks update`: Relax the bring-your-own VNet subnet validation for converting non-HOBO to HOBO Automatic cluster. `--apiserver-subnet-id` is no longer required, and `--system-node-subnet-id` can be supplied on its own; omitted subnets keep their current networking. `--enable-hosted-system` is still required to request the conversion, and `--node-subnet-id` still requires `--system-node-subnet-id`. + 22.0.0b2 ++++++++ * `az aks update`: Fix load balancer options such as `--load-balancer-backend-pool-type` being silently ignored on clusters whose `outboundType` is not `loadBalancer` (for example `userDefinedRouting` or a NAT gateway). Passing `--outbound-type` to switch away from `loadBalancer` now only drops the load balancer outbound settings instead of the whole load balancer profile, so options requested in the same command are still applied. diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index 4b2f2023dd5..84fa526185e 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -1591,26 +1591,26 @@ type: bool short-summary: (Automatic SKU) Convert an existing Automatic cluster to use a Managed System Pool. long-summary: | - Only valid for clusters with the Automatic SKU. Optionally provide the bring-your-own - VNet subnet trio (`--system-node-subnet-id`, `--node-subnet-id`, `--apiserver-subnet-id`) - to move the cluster onto an existing VNet at the same time. Supplying the full trio also - implies this flag. + Only valid for clusters with the Automatic SKU. Required to request the conversion. + Use it on its own to convert the cluster while keeping its current AKS-managed + networking, or combine it with the bring-your-own VNet subnet flags + (`--system-node-subnet-id`, `--node-subnet-id`, `--apiserver-subnet-id`) to move the + cluster onto an existing VNet at the same time. - name: --system-node-subnet-id type: string short-summary: (Automatic SKU) The ID of a subnet in an existing VNet to be used by the Managed System Pool. long-summary: | - Bring-your-own VNet for an Automatic cluster requires three subnets supplied together: - `--system-node-subnet-id` (this flag, for the Managed System Pool), `--node-subnet-id` - (for user node pools), and `--apiserver-subnet-id` (for the control plane API server). - All three subnets must belong to the same VNet. + Requires `--enable-hosted-system`. Unlike `az aks create`, the other bring-your-own + VNet subnets are optional here: `--node-subnet-id` (for user node pools) and + `--apiserver-subnet-id` (for the control plane API server) can be omitted, in which + case the cluster keeps its current node and API server networking. Any subnets you do + supply must belong to the same VNet. - name: --node-subnet-id type: string short-summary: (Automatic SKU) The ID of a subnet in an existing VNet to be used by user node pools. long-summary: | - Bring-your-own VNet for an Automatic cluster requires three subnets supplied together: - `--system-node-subnet-id` (for the Managed System Pool), `--node-subnet-id` (this flag, - for user node pools), and `--apiserver-subnet-id` (for the control plane API server). - All three subnets must belong to the same VNet. + Requires `--enable-hosted-system` and `--system-node-subnet-id`, and all supplied + subnets must belong to the same VNet. examples: - name: Reconcile the cluster back to its current state. text: az aks update -g MyResourceGroup -n MyManagedCluster @@ -1706,7 +1706,9 @@ text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-opentelemetry-logs-traces - name: Convert an existing Automatic cluster to use a Managed System Pool. text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-hosted-system - - name: Convert an existing Automatic cluster to use a Managed System Pool with a bring-your-own VNet. + - name: Convert an existing Automatic cluster to use a Managed System Pool in a bring-your-own VNet. + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-hosted-system --system-node-subnet-id + - name: Convert an existing Automatic cluster to use a Managed System Pool, also moving node pools and the API server onto a bring-your-own VNet. text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-hosted-system --system-node-subnet-id --node-subnet-id --apiserver-subnet-id """ 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 1d7fbf008ba..ef2ed62d055 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -4275,7 +4275,8 @@ def get_enable_hosted_system(self) -> bool: if self.decorator_mode not in (DecoratorMode.CREATE, DecoratorMode.UPDATE): return False explicit = bool(self.raw_param.get("enable_hosted_system")) - implicit = all( + # on update the conversion must be requested explicitly; subnets never imply it + implicit = self.decorator_mode == DecoratorMode.CREATE and all( [ self.raw_param.get("system_node_subnet_id"), self.raw_param.get("node_subnet_id"), @@ -4289,8 +4290,7 @@ def get_enable_hosted_system(self) -> bool: def get_system_node_subnet_id(self) -> Union[str, None]: """Obtain the value of system_node_subnet_id. - Validates that BYO VNet subnet flags are used as a full triple - (system-node / node / apiserver). + Cross-validates the BYO VNet subnet flags for the current decorator mode. :return: str or None """ @@ -4346,9 +4346,12 @@ def _hosted_system_sku_error_message(self) -> str: def validate_byo_hobo_subnet_trio(self) -> None: """Cross-validate the BYO VNet HOBO subnet flags. - Rule: if either --system-node-subnet-id or --node-subnet-id is set, the - full BYO trio must be set and --sku must be automatic. A complete trio - implies hosted-system enablement. + On create, setting either --system-node-subnet-id or --node-subnet-id requires the + full trio (both of those plus --apiserver-subnet-id), and a complete trio implies + hosted-system enablement. On update the conversion is requested explicitly with + --enable-hosted-system, so the subnets only shape the networking it lands on: + --node-subnet-id requires --system-node-subnet-id and --apiserver-subnet-id stays + optional. Either way the cluster must use the Automatic SKU. """ system_node_subnet_id = self.raw_param.get("system_node_subnet_id") node_subnet_id = self.raw_param.get("node_subnet_id") @@ -4358,6 +4361,19 @@ def validate_byo_hobo_subnet_trio(self) -> None: if enable_hosted_system and self.get_sku_name() != CONST_MANAGED_CLUSTER_SKU_NAME_AUTOMATIC: raise RequiredArgumentMissingError(self._hosted_system_sku_error_message()) + if self.decorator_mode == DecoratorMode.UPDATE: + if not self.has_byo_hobo_subnets(): + return + if node_subnet_id and not system_node_subnet_id: + raise RequiredArgumentMissingError( + '"--node-subnet-id" requires "--system-node-subnet-id".' + ) + if not enable_hosted_system: + raise RequiredArgumentMissingError( + 'Using "--system-node-subnet-id" and "--node-subnet-id" require "--enable-hosted-system".' + ) + return + if self.has_byo_hobo_subnets(): missing = [] if not system_node_subnet_id: @@ -4374,11 +4390,6 @@ def validate_byo_hobo_subnet_trio(self) -> None: f"Missing: {', '.join(missing)}." ) if self.get_sku_name() != CONST_MANAGED_CLUSTER_SKU_NAME_AUTOMATIC: - if self.decorator_mode == DecoratorMode.UPDATE: - raise RequiredArgumentMissingError( - '"--system-node-subnet-id" and "--node-subnet-id" are only supported on ' - "clusters with the Automatic SKU." - ) raise RequiredArgumentMissingError( '"--system-node-subnet-id" and "--node-subnet-id" require "--sku automatic".' ) @@ -8891,9 +8902,8 @@ def update_hosted_system_profile(self, mc: ManagedCluster) -> ManagedCluster: mc.hosted_system_profile = self.models.ManagedClusterHostedSystemProfile() # pylint: disable=no-member mc.hosted_system_profile.enabled = True - # BYO VNet: all three subnets (system-node / node / apiserver) must share a VNet, - # but the server enforces that check. The trio is already validated above, so read - # the raw values rather than the getters, which would re-run that validation. + # Already validated above, so read the raw values rather than the getters, + # which would re-run that validation. system_node_subnet_id = self.context.raw_param.get("system_node_subnet_id") node_subnet_id = self.context.raw_param.get("node_subnet_id") if system_node_subnet_id: 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 3e6826eb422..1917ea746d5 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 @@ -17319,14 +17319,14 @@ def test_update_hosted_system_profile(self): self.assertIsNone(dec_mc_1.hosted_system_profile.system_node_subnet_id) self.assertIsNone(dec_mc_1.hosted_system_profile.node_subnet_id) - # BYO conversion, the full subnet trio implies enablement + # BYO conversion dec_2 = AKSPreviewManagedClusterUpdateDecorator( self.cmd, self.client, { + "enable_hosted_system": True, "system_node_subnet_id": system_node_subnet_id, "node_subnet_id": node_subnet_id, - "apiserver_subnet_id": apiserver_subnet_id, }, CUSTOM_MGMT_AKS_PREVIEW, ) @@ -17340,8 +17340,8 @@ def test_update_hosted_system_profile(self): self.assertEqual(dec_mc_2.hosted_system_profile.system_node_subnet_id, system_node_subnet_id) self.assertEqual(dec_mc_2.hosted_system_profile.node_subnet_id, node_subnet_id) - # partial subnet trio is rejected - dec_3 = AKSPreviewManagedClusterUpdateDecorator( + # --node-subnet-id and --apiserver-subnet-id are optional + dec_6 = AKSPreviewManagedClusterUpdateDecorator( self.cmd, self.client, { @@ -17350,6 +17350,44 @@ def test_update_hosted_system_profile(self): }, CUSTOM_MGMT_AKS_PREVIEW, ) + mc_6 = self.models.ManagedCluster( + location="test_location", + sku=self.models.ManagedClusterSKU(name="Automatic"), + ) + dec_6.context.attach_mc(mc_6) + dec_mc_6 = dec_6.update_hosted_system_profile(mc_6) + self.assertTrue(dec_mc_6.hosted_system_profile.enabled) + self.assertEqual(dec_mc_6.hosted_system_profile.system_node_subnet_id, system_node_subnet_id) + self.assertIsNone(dec_mc_6.hosted_system_profile.node_subnet_id) + + # the subnet flags require --enable-hosted-system + dec_9 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "system_node_subnet_id": system_node_subnet_id, + "node_subnet_id": node_subnet_id, + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_9 = self.models.ManagedCluster( + location="test_location", + sku=self.models.ManagedClusterSKU(name="Automatic"), + ) + dec_9.context.attach_mc(mc_9) + with self.assertRaises(RequiredArgumentMissingError): + dec_9.update_hosted_system_profile(mc_9) + + # --node-subnet-id requires --system-node-subnet-id + dec_3 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_hosted_system": True, + "node_subnet_id": node_subnet_id, + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) mc_3 = self.models.ManagedCluster( location="test_location", sku=self.models.ManagedClusterSKU(name="Automatic"), @@ -17358,6 +17396,32 @@ def test_update_hosted_system_profile(self): with self.assertRaises(RequiredArgumentMissingError): dec_3.update_hosted_system_profile(mc_3) + # --apiserver-subnet-id may still be supplied alongside the node subnets + dec_5 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_hosted_system": True, + "system_node_subnet_id": system_node_subnet_id, + "node_subnet_id": node_subnet_id, + "apiserver_subnet_id": apiserver_subnet_id, + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_5 = self.models.ManagedCluster( + location="test_location", + sku=self.models.ManagedClusterSKU(name="Automatic"), + api_server_access_profile=self.models.ManagedClusterAPIServerAccessProfile( + enable_vnet_integration=True, + subnet_id=apiserver_subnet_id, + ), + ) + dec_5.context.attach_mc(mc_5) + dec_mc_5 = dec_5.update_hosted_system_profile(mc_5) + self.assertTrue(dec_mc_5.hosted_system_profile.enabled) + self.assertEqual(dec_mc_5.hosted_system_profile.system_node_subnet_id, system_node_subnet_id) + self.assertEqual(dec_mc_5.hosted_system_profile.node_subnet_id, node_subnet_id) + # non-Automatic SKU is rejected dec_4 = AKSPreviewManagedClusterUpdateDecorator( self.cmd, diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index df53effcdbc..8e7c3d36a83 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "22.0.0b2" +VERSION = "22.0.0b3" CLASSIFIERS = [ "Development Status :: 4 - Beta",