This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.22 by this push:
     new 7a11bd2f987 CKS: Fix issue with scaling down CKS Nodes when deployed 
in HA mode (#12302)
7a11bd2f987 is described below

commit 7a11bd2f987c8b1e50f84e67ef1f4185773a2ed0
Author: Pearl Dsilva <[email protected]>
AuthorDate: Tue Dec 30 07:08:13 2025 -0500

    CKS: Fix issue with scaling down CKS Nodes when deployed in HA mode (#12302)
---
 .../KubernetesClusterScaleWorker.java              | 24 ++++++--
 .../KubernetesClusterScaleWorkerTest.java          | 66 +++++++++++++++++++++-
 2 files changed, 83 insertions(+), 7 deletions(-)

diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
index dba858ed809..6d22a1a3a03 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
@@ -441,16 +441,30 @@ public class KubernetesClusterScaleWorker extends 
KubernetesClusterResourceModif
         if (this.nodeIds != null) {
             vmList = 
getKubernetesClusterVMMapsForNodes(this.nodeIds).stream().filter(vm -> 
!vm.isExternalNode()).collect(Collectors.toList());
         } else {
-            vmList  = getKubernetesClusterVMMaps();
-            vmList  = vmList.stream()
-                        .filter(vm -> !vm.isExternalNode() && 
!vm.isControlNode() && !vm.isEtcdNode())
-                        .collect(Collectors.toList());
-            vmList = vmList.subList((int) 
(kubernetesCluster.getControlNodeCount() + clusterSize - 1), vmList.size());
+            vmList = getWorkerNodesToRemove();
+            if (vmList.isEmpty()) {
+                logger.info("No nodes to remove from Kubernetes cluster: {}", 
kubernetesCluster);
+                return;
+            }
         }
         Collections.reverse(vmList);
         removeNodesFromCluster(vmList);
     }
 
+    public List<KubernetesClusterVmMapVO> getWorkerNodesToRemove() {
+        List<KubernetesClusterVmMapVO> workerVMsMap = 
getKubernetesClusterVMMaps().stream()
+                .filter(vm -> !vm.isExternalNode() && !vm.isControlNode() && 
!vm.isEtcdNode())
+                .collect(Collectors.toList());
+        int totalWorkerNodes = workerVMsMap.size();
+        int desiredWorkerNodes = clusterSize == null ? (int) 
kubernetesCluster.getNodeCount() : clusterSize.intValue();
+        int toRemoveCount = Math.max(0, totalWorkerNodes - desiredWorkerNodes);
+        if (toRemoveCount == 0) {
+            return new ArrayList<>();
+        }
+        int startIndex = Math.max(0, totalWorkerNodes - toRemoveCount);
+        return new ArrayList<>(workerVMsMap.subList(startIndex, 
totalWorkerNodes));
+    }
+
     private void scaleUpKubernetesClusterSize(final long newVmCount) throws 
CloudRuntimeException {
         if 
(!kubernetesCluster.getState().equals(KubernetesCluster.State.Scaling)) {
             stateTransitTo(kubernetesCluster.getId(), 
KubernetesCluster.Event.ScaleUpRequested);
diff --git 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorkerTest.java
 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorkerTest.java
index 847c8bd6d29..c9299bdbaa6 100644
--- 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorkerTest.java
+++ 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorkerTest.java
@@ -17,8 +17,8 @@
 package com.cloud.kubernetes.cluster.actionworkers;
 
 import com.cloud.kubernetes.cluster.KubernetesCluster;
-import com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl;
 import com.cloud.kubernetes.cluster.KubernetesClusterVmMapVO;
+import com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl;
 import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.service.ServiceOfferingVO;
@@ -29,15 +29,17 @@ import com.cloud.vm.dao.UserVmDao;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.Arrays;
 import java.util.List;
 
-import static 
com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.DEFAULT;
 import static 
com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.CONTROL;
+import static 
com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.DEFAULT;
 
 @RunWith(MockitoJUnitRunner.class)
 public class KubernetesClusterScaleWorkerTest {
@@ -125,4 +127,64 @@ public class KubernetesClusterScaleWorkerTest {
         Assert.assertEquals(expectedCores, 
newClusterCapacity.first().longValue());
         Assert.assertEquals(expectedMemory, 
newClusterCapacity.second().longValue());
     }
+
+
+    @Test
+    public void testGetWorkerNodesToRemoveForDownsize_singleRemoval() {
+        KubernetesCluster kubernetesCluster = 
Mockito.mock(KubernetesCluster.class);
+        KubernetesClusterManagerImpl clusterManager = 
Mockito.mock(KubernetesClusterManagerImpl.class);
+        KubernetesClusterScaleWorker worker = new 
KubernetesClusterScaleWorker(kubernetesCluster, new java.util.HashMap<>(), 2L, 
null, false, null, null, clusterManager);
+        KubernetesClusterScaleWorker spyWorker = Mockito.spy(worker);
+
+        KubernetesClusterVmMapVO vm1 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm1.isExternalNode()).thenReturn(false);
+        Mockito.when(vm1.isControlNode()).thenReturn(false);
+        Mockito.when(vm1.isEtcdNode()).thenReturn(false);
+
+        KubernetesClusterVmMapVO vm2 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm2.isExternalNode()).thenReturn(false);
+        Mockito.when(vm2.isControlNode()).thenReturn(false);
+        Mockito.when(vm2.isEtcdNode()).thenReturn(false);
+
+        KubernetesClusterVmMapVO vm3 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm3.isExternalNode()).thenReturn(false);
+        Mockito.when(vm3.isControlNode()).thenReturn(false);
+        Mockito.when(vm3.isEtcdNode()).thenReturn(false);
+
+        Mockito.doReturn(Arrays.asList(vm1, vm2, 
vm3)).when(spyWorker).getKubernetesClusterVMMaps();
+
+        List<KubernetesClusterVmMapVO> toRemove = 
spyWorker.getWorkerNodesToRemove();
+
+        Assert.assertEquals(1, toRemove.size());
+        Assert.assertSame(vm3, toRemove.get(0));
+    }
+
+    @Test
+    public void testGetWorkerNodesToRemoveForDownsize_noRemoval() {
+        KubernetesCluster kubernetesCluster = 
Mockito.mock(KubernetesCluster.class);
+
+        KubernetesClusterScaleWorker worker = new 
KubernetesClusterScaleWorker(kubernetesCluster, new java.util.HashMap<>(), 3L, 
null, false, null, null, clusterManager);
+        KubernetesClusterScaleWorker spyWorker = Mockito.spy(worker);
+
+        KubernetesClusterVmMapVO vm1 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm1.isExternalNode()).thenReturn(false);
+        Mockito.when(vm1.isControlNode()).thenReturn(false);
+        Mockito.when(vm1.isEtcdNode()).thenReturn(false);
+
+        KubernetesClusterVmMapVO vm2 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm2.isExternalNode()).thenReturn(false);
+        Mockito.when(vm2.isControlNode()).thenReturn(false);
+        Mockito.when(vm2.isEtcdNode()).thenReturn(false);
+
+        KubernetesClusterVmMapVO vm3 = 
Mockito.mock(KubernetesClusterVmMapVO.class);
+        Mockito.when(vm3.isExternalNode()).thenReturn(false);
+        Mockito.when(vm3.isControlNode()).thenReturn(false);
+        Mockito.when(vm3.isEtcdNode()).thenReturn(false);
+
+        Mockito.doReturn(Arrays.asList(vm1, vm2, 
vm3)).when(spyWorker).getKubernetesClusterVMMaps();
+
+        List<KubernetesClusterVmMapVO> toRemove = 
spyWorker.getWorkerNodesToRemove();
+
+        Assert.assertTrue(toRemove.isEmpty());
+    }
 }

Reply via email to