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

Reply via email to