CKS: Fix issue with scaling down CKS Nodes when deployed in HA mode (#12302)

This commit is contained in:
Pearl Dsilva 2025-12-30 07:08:13 -05:00 committed by GitHub
parent 6a2d91ed8a
commit 7a11bd2f98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 7 deletions

View File

@ -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);

View File

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