This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push:
new da518e90362 CKS: Add image store validation for Kubernetes version
registration (#12418)
da518e90362 is described below
commit da518e903621187e4cb75af9f550b7c6d57666f9
Author: Daman Arora <[email protected]>
AuthorDate: Tue Jan 20 02:13:15 2026 -0500
CKS: Add image store validation for Kubernetes version registration (#12418)
Co-authored-by: Daman Arora <[email protected]>
---
.../version/KubernetesVersionManagerImpl.java | 33 ++++++++++
.../version/KubernetesVersionManagerImplTest.java | 72 ++++++++++++++++++++++
.../version/KubernetesVersionServiceTest.java | 9 +++
3 files changed, 114 insertions(+)
diff --git
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
index 7b126b2fba0..8363f6f87e3 100644
---
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
+++
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
@@ -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.ListResponse;
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 com.cloud.api.query.dao.TemplateJoinDao;
@@ -81,6 +84,8 @@ public class KubernetesVersionManagerImpl extends ManagerBase
implements Kuberne
@Inject
private DataCenterDao dataCenterDao;
@Inject
+ private ImageStoreDao imageStoreDao;
+ @Inject
private TemplateApiService templateService;
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());
}
+ 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
@ActionEvent(eventType =
KubernetesVersionEventTypes.EVENT_KUBERNETES_VERSION_ADD,
eventDescription = "Adding Kubernetes supported version")
@@ -368,6 +399,8 @@ public class KubernetesVersionManagerImpl extends
ManagerBase implements Kuberne
}
}
+ validateImageStoreForZone(zoneId, isDirectDownload);
+
VMTemplateVO template = null;
try {
VirtualMachineTemplate vmTemplate =
registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum,
isDirectDownload, arch);
diff --git
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionManagerImplTest.java
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionManagerImplTest.java
index bbec555e8e5..35f8e66e045 100644
---
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionManagerImplTest.java
+++
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionManagerImplTest.java
@@ -16,10 +16,15 @@
// under the License.
package com.cloud.kubernetes.version;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.UUID;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
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.Test;
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.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.exception.InvalidParameterValueException;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionManagerImplTest {
@@ -39,6 +47,12 @@ public class KubernetesVersionManagerImplTest {
@Mock
TemplateJoinDao templateJoinDao;
+ @Mock
+ ImageStoreDao imageStoreDao;
+
+ @Mock
+ DataCenterDao dataCenterDao;
+
@InjectMocks
KubernetesVersionManagerImpl kubernetesVersionManager = new
KubernetesVersionManagerImpl();
@@ -72,4 +86,62 @@ public class KubernetesVersionManagerImplTest {
response, true);
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);
+ }
}
diff --git
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
index b874a9a0ffa..7ba35169b9e 100644
---
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
+++
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
@@ -78,6 +78,9 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
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)
public class KubernetesVersionServiceTest {
@@ -97,6 +100,8 @@ public class KubernetesVersionServiceTest {
@Mock
private DataCenterDao dataCenterDao;
@Mock
+ private ImageStoreDao imageStoreDao;
+ @Mock
private TemplateApiService templateService;
@Mock
private Account accountMock;
@@ -128,6 +133,10 @@ public class KubernetesVersionServiceTest {
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
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);
when(templateJoinVO.getUrl()).thenReturn("https://download.cloudstack.com");
when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);