CKS: Add image store validation for Kubernetes version registration (#12418)

Co-authored-by: Daman Arora <daman.arora@shapeblue.com>
This commit is contained in:
Daman Arora 2026-01-20 02:13:15 -05:00 committed by GitHub
parent 03d24ff851
commit da518e9036
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 114 additions and 0 deletions

View File

@ -33,6 +33,9 @@ import org.apache.cloudstack.api.command.user.kubernetes.version.ListKubernetesS
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse; import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.cloud.api.query.dao.TemplateJoinDao; import com.cloud.api.query.dao.TemplateJoinDao;
@ -81,6 +84,8 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
@Inject @Inject
private DataCenterDao dataCenterDao; private DataCenterDao dataCenterDao;
@Inject @Inject
private ImageStoreDao imageStoreDao;
@Inject
private TemplateApiService templateService; private TemplateApiService templateService;
public static final String MINIMUN_AUTOSCALER_SUPPORTED_VERSION = "1.15.0"; public static final String MINIMUN_AUTOSCALER_SUPPORTED_VERSION = "1.15.0";
@ -323,6 +328,32 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
return createKubernetesSupportedVersionListResponse(versions, versionsAndCount.second()); return createKubernetesSupportedVersionListResponse(versions, versionsAndCount.second());
} }
private void validateImageStoreForZone(Long zoneId, boolean directDownload) {
if (directDownload) {
return;
}
if (zoneId != null) {
List<ImageStoreVO> imageStores = imageStoreDao.listStoresByZoneId(zoneId);
if (CollectionUtils.isEmpty(imageStores)) {
DataCenterVO zone = dataCenterDao.findById(zoneId);
String zoneName = zone != null ? zone.getName() : String.valueOf(zoneId);
throw new InvalidParameterValueException(String.format("Unable to register Kubernetes version ISO. No image store available in zone: %s", zoneName));
}
} else {
List<DataCenterVO> zones = dataCenterDao.listAllZones();
List<String> zonesWithoutStorage = new ArrayList<>();
for (DataCenterVO zone : zones) {
List<ImageStoreVO> imageStores = imageStoreDao.listStoresByZoneId(zone.getId());
if (CollectionUtils.isEmpty(imageStores)) {
zonesWithoutStorage.add(zone.getName());
}
}
if (!zonesWithoutStorage.isEmpty()) {
throw new InvalidParameterValueException(String.format("Unable to register Kubernetes version ISO for all zones. The following zones have no image store: %s", String.join(", ", zonesWithoutStorage)));
}
}
}
@Override @Override
@ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_ADD, @ActionEvent(eventType = KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_ADD,
eventDescription = "Adding Kubernetes supported version") eventDescription = "Adding Kubernetes supported version")
@ -368,6 +399,8 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
} }
} }
validateImageStoreForZone(zoneId, isDirectDownload);
VMTemplateVO template = null; VMTemplateVO template = null;
try { try {
VirtualMachineTemplate vmTemplate = registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, isDirectDownload, arch); VirtualMachineTemplate vmTemplate = registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, isDirectDownload, arch);

View File

@ -16,10 +16,15 @@
// under the License. // under the License.
package com.cloud.kubernetes.version; package com.cloud.kubernetes.version;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse; import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -32,6 +37,9 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.api.query.dao.TemplateJoinDao; import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU; import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.InvalidParameterValueException;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionManagerImplTest { public class KubernetesVersionManagerImplTest {
@ -39,6 +47,12 @@ public class KubernetesVersionManagerImplTest {
@Mock @Mock
TemplateJoinDao templateJoinDao; TemplateJoinDao templateJoinDao;
@Mock
ImageStoreDao imageStoreDao;
@Mock
DataCenterDao dataCenterDao;
@InjectMocks @InjectMocks
KubernetesVersionManagerImpl kubernetesVersionManager = new KubernetesVersionManagerImpl(); KubernetesVersionManagerImpl kubernetesVersionManager = new KubernetesVersionManagerImpl();
@ -72,4 +86,62 @@ public class KubernetesVersionManagerImplTest {
response, true); response, true);
Assert.assertEquals(state.toString(), ReflectionTestUtils.getField(response, "isoState")); Assert.assertEquals(state.toString(), ReflectionTestUtils.getField(response, "isoState"));
} }
@Test
public void testValidateImageStoreForZoneWithDirectDownload() {
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", 1L, true);
}
@Test
public void testValidateImageStoreForZoneWithValidZone() {
Long zoneId = 1L;
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(zoneId)).thenReturn(imageStores);
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", zoneId, false);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateImageStoreForZoneWithNoImageStore() {
Long zoneId = 1L;
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
Mockito.when(zone.getName()).thenReturn("test-zone");
Mockito.when(dataCenterDao.findById(zoneId)).thenReturn(zone);
Mockito.when(imageStoreDao.listStoresByZoneId(zoneId)).thenReturn(Collections.emptyList());
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", zoneId, false);
}
@Test
public void testValidateImageStoreForAllZonesWithAllValid() {
DataCenterVO zone1 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone1.getId()).thenReturn(1L);
DataCenterVO zone2 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone2.getId()).thenReturn(2L);
List<DataCenterVO> zones = Arrays.asList(zone1, zone2);
Mockito.when(dataCenterDao.listAllZones()).thenReturn(zones);
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(1L)).thenReturn(imageStores);
Mockito.when(imageStoreDao.listStoresByZoneId(2L)).thenReturn(imageStores);
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", (Long) null, false);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateImageStoreForAllZonesWithSomeMissingStorage() {
DataCenterVO zone1 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone1.getId()).thenReturn(1L);
DataCenterVO zone2 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone2.getId()).thenReturn(2L);
Mockito.when(zone2.getName()).thenReturn("zone-without-storage");
List<DataCenterVO> zones = Arrays.asList(zone1, zone2);
Mockito.when(dataCenterDao.listAllZones()).thenReturn(zones);
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(1L)).thenReturn(imageStores);
Mockito.when(imageStoreDao.listStoresByZoneId(2L)).thenReturn(Collections.emptyList());
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", (Long) null, false);
}
} }

View File

@ -78,6 +78,9 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionServiceTest { public class KubernetesVersionServiceTest {
@ -97,6 +100,8 @@ public class KubernetesVersionServiceTest {
@Mock @Mock
private DataCenterDao dataCenterDao; private DataCenterDao dataCenterDao;
@Mock @Mock
private ImageStoreDao imageStoreDao;
@Mock
private TemplateApiService templateService; private TemplateApiService templateService;
@Mock @Mock
private Account accountMock; private Account accountMock;
@ -128,6 +133,10 @@ public class KubernetesVersionServiceTest {
DataCenterVO zone = Mockito.mock(DataCenterVO.class); DataCenterVO zone = Mockito.mock(DataCenterVO.class);
when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone);
List<ImageStoreVO> imageStores = new ArrayList<>();
imageStores.add(Mockito.mock(ImageStoreVO.class));
when(imageStoreDao.listStoresByZoneId(Mockito.anyLong())).thenReturn(imageStores);
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class); TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
when(templateJoinVO.getUrl()).thenReturn("https://download.cloudstack.com"); when(templateJoinVO.getUrl()).thenReturn("https://download.cloudstack.com");
when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready); when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);