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());
+ }
}