server,cks: check if vm is cks node during vm destroy (#9057)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2024-06-06 14:24:02 +05:30 committed by GitHub
parent 631d6ad09b
commit 91c7bc722f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 163 additions and 5 deletions

View File

@ -16,10 +16,13 @@
// under the License.
package com.cloud.kubernetes.cluster;
import com.cloud.utils.component.Adapter;
import org.apache.cloudstack.acl.ControlledEntity;
import com.cloud.uservm.UserVm;
import com.cloud.utils.component.Adapter;
public interface KubernetesClusterHelper extends Adapter {
ControlledEntity findByUuid(String uuid);
void checkVmCanBeDestroyed(UserVm userVm);
}

View File

@ -48,4 +48,6 @@ public interface UserVm extends VirtualMachine, ControlledEntity {
void setAccountId(long accountId);
public boolean isDisplayVm();
String getUserVmType();
}

View File

@ -148,6 +148,7 @@ public class UserVmVO extends VMInstanceVO implements UserVm {
return updateParameters;
}
@Override
public String getUserVmType() {
return userVmType;
}

View File

@ -16,26 +16,55 @@
// under the License.
package com.cloud.kubernetes.cluster;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
import com.cloud.utils.component.AdapterBase;
import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.UserVmManager;
@Component
public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable {
private static final Logger logger = Logger.getLogger(KubernetesClusterHelperImpl.class);
@Inject
private KubernetesClusterDao kubernetesClusterDao;
@Inject
private KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
@Override
public ControlledEntity findByUuid(String uuid) {
return kubernetesClusterDao.findByUuid(uuid);
}
@Override
public void checkVmCanBeDestroyed(UserVm userVm) {
if (!UserVmManager.CKS_NODE.equals(userVm.getUserVmType())) {
return;
}
KubernetesClusterVmMapVO vmMapVO = kubernetesClusterVmMapDao.findByVmId(userVm.getId());
if (vmMapVO == null) {
return;
}
logger.error(String.format("VM ID: %s is a part of Kubernetes cluster ID: %d", userVm.getId(), vmMapVO.getClusterId()));
KubernetesCluster kubernetesCluster = kubernetesClusterDao.findById(vmMapVO.getClusterId());
String msg = "Instance is a part of a Kubernetes cluster";
if (kubernetesCluster != null) {
msg += String.format(": %s", kubernetesCluster.getName());
}
msg += ". Use Instance delete option from Kubernetes cluster details or scale API for " +
"Kubernetes clusters with 'nodeids' to destroy the instance.";
throw new CloudRuntimeException(msg);
}
@Override
public String getConfigComponentName() {
return KubernetesClusterHelper.class.getSimpleName();

View File

@ -28,4 +28,6 @@ public interface KubernetesClusterVmMapDao extends GenericDao<KubernetesClusterV
int removeByClusterIdAndVmIdsIn(long clusterId, List<Long> vmIds);
public int removeByClusterId(long clusterId);
KubernetesClusterVmMapVO findByVmId(long vmId);
}

View File

@ -69,4 +69,14 @@ public class KubernetesClusterVmMapDaoImpl extends GenericDaoBase<KubernetesClus
sc.setParameters("clusterId", clusterId);
return remove(sc);
}
@Override
public KubernetesClusterVmMapVO findByVmId(long vmId) {
SearchBuilder<KubernetesClusterVmMapVO> sb = createSearchBuilder();
sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<KubernetesClusterVmMapVO> sc = sb.create();
sc.setParameters("vmId", vmId);
return findOneBy(sc);
}
}

View File

@ -0,0 +1,71 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.kubernetes.cluster;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.UserVmManager;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesClusterHelperImplTest {
@Mock
KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
@Mock
KubernetesClusterDao kubernetesClusterDao;
@InjectMocks
KubernetesClusterHelperImpl kubernetesClusterHelper = new KubernetesClusterHelperImpl();
@Test
public void testCheckVmCanBeDestroyedNotCKSNode() {
UserVm vm = Mockito.mock(UserVm.class);
Mockito.when(vm.getUserVmType()).thenReturn("");
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
Mockito.verify(kubernetesClusterVmMapDao, Mockito.never()).findByVmId(Mockito.anyLong());
}
@Test
public void testCheckVmCanBeDestroyedNotInCluster() {
UserVm vm = Mockito.mock(UserVm.class);
Mockito.when(vm.getId()).thenReturn(1L);
Mockito.when(vm.getUserVmType()).thenReturn(UserVmManager.CKS_NODE);
Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(null);
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
}
@Test(expected = CloudRuntimeException.class)
public void testCheckVmCanBeDestroyedInCluster() {
UserVm vm = Mockito.mock(UserVm.class);
Mockito.when(vm.getId()).thenReturn(1L);
Mockito.when(vm.getUserVmType()).thenReturn(UserVmManager.CKS_NODE);
KubernetesClusterVmMapVO map = Mockito.mock(KubernetesClusterVmMapVO.class);
Mockito.when(map.getClusterId()).thenReturn(1L);
Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(map);
Mockito.when(kubernetesClusterDao.findById(1L)).thenReturn(Mockito.mock(KubernetesClusterVO.class));
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
}
}

View File

@ -130,8 +130,8 @@ import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.userdata.UserDataManager;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
import org.apache.cloudstack.utils.security.ParserUtils;
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
import org.apache.cloudstack.vm.UnmanagedVMsManager;
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
@ -141,6 +141,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.w3c.dom.Document;
@ -241,6 +242,7 @@ import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
import com.cloud.kubernetes.cluster.KubernetesClusterHelper;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.Network.GuestType;
@ -346,6 +348,7 @@ import com.cloud.utils.DateUtil;
import com.cloud.utils.Journal;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.crypt.DBEncryptionUtil;
@ -595,6 +598,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Inject
VMScheduleManager vmScheduleManager;
private ScheduledExecutorService _executor = null;
private ScheduledExecutorService _vmIpFetchExecutor = null;
private int _expungeInterval;
@ -3280,6 +3284,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return null;
}
protected void checkPluginsIfVmCanBeDestroyed(UserVm vm) {
try {
KubernetesClusterHelper kubernetesClusterHelper =
ComponentContext.getDelegateComponentOfType(KubernetesClusterHelper.class);
kubernetesClusterHelper.checkVmCanBeDestroyed(vm);
} catch (NoSuchBeanDefinitionException ignored) {
s_logger.debug("No KubernetesClusterHelper bean found");
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_DESTROY, eventDescription = "destroying Vm", async = true)
public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException {
@ -3306,6 +3320,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// check if vm belongs to AutoScale vm group in Disabled state
autoScaleManager.checkIfVmActionAllowed(vmId);
// check if vm belongs to any plugin resources
checkPluginsIfVmCanBeDestroyed(vm);
// check if there are active volume snapshots tasks
s_logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId);
if (checkStatusOfVolumeSnapshots(vmId, Volume.Type.ROOT)) {

View File

@ -226,6 +226,10 @@
<artifactId>tink</artifactId>
<version>${cs.tink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -29,6 +29,7 @@ import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.naming.ConfigurationException;
import org.apache.commons.collections4.MapUtils;
import org.apache.log4j.Logger;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.BeansException;
@ -286,4 +287,22 @@ public class ComponentContext implements ApplicationContextAware {
private static synchronized void initInitializeBeans(boolean initializeBeans) {
s_initializeBeans = initializeBeans;
}
public static <T> T getDelegateComponentOfType(Class<T> beanType) {
if (s_appContextDelegates == null) {
throw new NoSuchBeanDefinitionException(beanType.getName());
}
T bean = null;
for (ApplicationContext context : s_appContextDelegates.values()) {
Map<String, T> map = context.getBeansOfType(beanType);
if (MapUtils.isNotEmpty(map)) {
bean = (T)map.values().toArray()[0];
break;
}
}
if (bean == null) {
throw new NoSuchBeanDefinitionException(beanType.getName());
}
return bean;
}
}