diff --git a/src/azure-cli/azure/cli/command_modules/acs/_consts.py b/src/azure-cli/azure/cli/command_modules/acs/_consts.py index 5244002b05e..227790750ee 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_consts.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_consts.py @@ -26,6 +26,7 @@ CONST_NODEPOOL_MODE_SYSTEM = "System" CONST_NODEPOOL_MODE_USER = "User" CONST_NODEPOOL_MODE_GATEWAY = "Gateway" +CONST_NODEPOOL_MODE_MACHINES = "Machines" # os type CONST_DEFAULT_NODE_OS_TYPE = "Linux" diff --git a/src/azure-cli/azure/cli/command_modules/acs/custom.py b/src/azure-cli/azure/cli/command_modules/acs/custom.py index ea653eb45bd..bd8c3051d9c 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/custom.py @@ -61,6 +61,7 @@ CONST_MONITORING_ADDON_NAME, CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID, CONST_MONITORING_USING_AAD_MSI_AUTH, + CONST_NODEPOOL_MODE_MACHINES, CONST_NODEPOOL_MODE_USER, CONST_OPEN_SERVICE_MESH_ADDON_NAME, CONST_ROTATION_POLL_INTERVAL, @@ -1320,6 +1321,10 @@ def aks_upgrade(cmd, if vmas_cluster: raise CLIError('This cluster is using AvailabilitySet. Node image upgrade only operation ' 'can only be applied on VirtualMachineScaleSets or VirtualMachines cluster.') + # Skip Machines mode pools to avoid a known client-side error: these pools are containers of individual machines and do not support node image version upgrade. + if (agent_pool_profile.mode or "").lower() == CONST_NODEPOOL_MODE_MACHINES.lower(): + logger.warning("Skipping node image upgrade for agent pool '%s': Machines mode pools do not support node image version upgrade.", agent_pool_profile.name) + continue agent_pool_client = cf_agent_pools(cmd.cli_ctx) _upgrade_single_nodepool_image_version(True, agent_pool_client, resource_group_name, name, agent_pool_profile.name) @@ -1381,6 +1386,10 @@ def aks_upgrade(cmd, if upgrade_all: for agent_profile in (instance.agent_pool_profiles or []): + # Skip Machines mode pools to avoid a known client-side error: these pools are containers of individual machines and do not support Kubernetes version upgrade. + if (agent_profile.mode or "").lower() == CONST_NODEPOOL_MODE_MACHINES.lower(): + logger.warning("Skipping Kubernetes version upgrade for agent pool '%s': Machines mode pools do not support Kubernetes version upgrade.", agent_profile.name) + continue agent_profile.orchestrator_version = kubernetes_version agent_profile.creation_data = None diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py index c2f7cc14c83..694278de645 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py @@ -30,6 +30,7 @@ aks_agentpool_upgrade, aks_enable_addons, aks_stop, + aks_upgrade, is_monitoring_addon_enabled, k8s_install_kubectl, k8s_install_kubelogin, @@ -971,6 +972,51 @@ def test_aks_stop(self): ) self.assertEqual(aks_stop(self.cmd, self.client, "rg", "name", False), None) + def test_aks_upgrade_node_image_only_skips_machines_mode_pool(self): + """Machines mode pools must be skipped during --node-image-only to avoid a known client-side error.""" + machines_pool = self.models.ManagedClusterAgentPoolProfile(name="machinespool", mode="Machines", type="VirtualMachines") + vmss_pool = self.models.ManagedClusterAgentPoolProfile(name="nodepool1", mode="User", type="VirtualMachineScaleSets") + mc = self.models.ManagedCluster(location="test_location") + mc.agent_pool_profiles = [machines_pool, vmss_pool] + mc.pod_identity_profile = None + mc.kubernetes_version = "1.24.0" + mc.provisioning_state = "Succeeded" + mc.max_agent_pools = 10 + + self.client.get = mock.Mock(return_value=mc) + + with mock.patch("azure.cli.command_modules.acs.custom.cf_agent_pools") as mock_cf, \ + mock.patch("azure.cli.command_modules.acs.custom._upgrade_single_nodepool_image_version") as mock_upgrade: + mock_cf.return_value = mock.Mock() + + aks_upgrade(self.cmd, self.client, "rg", "name", node_image_only=True, yes=True) + + # Only the VMSS pool should be upgraded; the Machines mode pool must be skipped. + upgraded_pools = [call.args[4] for call in mock_upgrade.call_args_list] + self.assertNotIn("machinespool", upgraded_pools) + self.assertIn("nodepool1", upgraded_pools) + + def test_aks_upgrade_kubernetes_version_skips_machines_mode_pool(self): + """Machines mode pools must be skipped during Kubernetes version upgrade to avoid a known client-side error.""" + machines_pool = self.models.ManagedClusterAgentPoolProfile(name="machinespool", mode="Machines", type="VirtualMachines") + vmss_pool = self.models.ManagedClusterAgentPoolProfile(name="nodepool1", mode="User", type="VirtualMachineScaleSets") + mc = self.models.ManagedCluster(location="test_location") + mc.agent_pool_profiles = [machines_pool, vmss_pool] + mc.pod_identity_profile = None + mc.kubernetes_version = "1.24.0" + mc.provisioning_state = "Succeeded" + mc.max_agent_pools = 10 + mc.service_principal_profile = None + + self.client.get = mock.Mock(return_value=mc) + self.client.begin_create_or_update = mock.Mock(return_value=None) + + aks_upgrade(self.cmd, self.client, "rg", "name", kubernetes_version="1.25.0", yes=True) + + # Machines mode pool must not have orchestrator_version set; VMSS pool must be upgraded. + self.assertIsNone(machines_pool.orchestrator_version) + self.assertEqual(vmss_pool.orchestrator_version, "1.25.0") + class TestRunCommand(unittest.TestCase): def test_get_command_context_invalid_file(self):