This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch fr06-cks-template-register
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 967b5029bea81e34d8f15a22224dee16090f9e98
Author: nvazquez <nicovazque...@gmail.com>
AuthorDate: Sun Feb 4 01:32:07 2024 -0300

    Persist values for managed clusters
---
 .../cluster/KubernetesClusterManagerImpl.java      | 129 +++++++++++++++++----
 .../kubernetes/cluster/KubernetesClusterVO.java    |  44 +++++++
 .../cluster/CreateKubernetesClusterCmd.java        |  12 +-
 .../cluster/KubernetesClusterManagerImplTest.java  |  71 ++++++++++++
 .../cluster/CreateKubernetesClusterCmdTest.java    |  18 +--
 5 files changed, 234 insertions(+), 40 deletions(-)

diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
index fcb208c6718..c7de5dbcd2e 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
@@ -16,6 +16,9 @@
 // under the License.
 package com.cloud.kubernetes.cluster;
 
+import static 
com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.CONTROL;
+import static 
com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.ETCD;
+import static 
com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.WORKER;
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
 import static com.cloud.vm.UserVmManager.AllowUserExpungeRecoverVm;
 
@@ -72,6 +75,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
@@ -192,6 +196,8 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
 
     protected StateMachine2<KubernetesCluster.State, KubernetesCluster.Event, 
KubernetesCluster> _stateMachine = KubernetesCluster.State.getStateMachine();
 
+    protected final static List<String> CLUSTER_NODES_TYPES_LIST = 
Arrays.asList(WORKER.name(), CONTROL.name(), ETCD.name());
+
     ScheduledExecutorService _gcExecutor;
     ScheduledExecutorService _stateScanner;
 
@@ -446,7 +452,7 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         validateIsolatedNetwork(network, clusterTotalNodeCount);
     }
 
-    private boolean validateServiceOffering(final ServiceOffering 
serviceOffering, final KubernetesSupportedVersion version) {
+    protected void validateServiceOffering(final ServiceOffering 
serviceOffering, final KubernetesSupportedVersion version) throws 
InvalidParameterValueException {
         if (serviceOffering.isDynamic()) {
             throw new InvalidParameterValueException(String.format("Custom 
service offerings are not supported for creating clusters, service offering ID: 
%s", serviceOffering.getUuid()));
         }
@@ -459,7 +465,6 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         if (serviceOffering.getRamSize() < version.getMinimumRamSize()) {
             throw new InvalidParameterValueException(String.format("Kubernetes 
cluster cannot be created with service offering ID: %s, associated Kubernetes 
version ID: %s needs minimum %d MB RAM", serviceOffering.getUuid(), 
version.getUuid(), version.getMinimumRamSize()));
         }
-        return true;
     }
 
     private void validateDockerRegistryParams(final String 
dockerRegistryUserName,
@@ -736,7 +741,6 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         final String name = cmd.getName();
         final Long zoneId = cmd.getZoneId();
         final Long kubernetesVersionId = cmd.getKubernetesVersionId();
-        final Long serviceOfferingId = cmd.getServiceOfferingId();
         final Account owner = 
accountService.getActiveAccountById(cmd.getEntityOwnerId());
         final Long networkId = cmd.getNetworkId();
         final String sshKeyPair = cmd.getSSHKeyPairName();
@@ -747,6 +751,7 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         final String dockerRegistryUrl = cmd.getDockerRegistryUrl();
         final Long nodeRootDiskSize = cmd.getNodeRootDiskSize();
         final String externalLoadBalancerIpAddress = 
cmd.getExternalLoadBalancerIpAddress();
+        final Map<String, Long> serviceOfferingNodeTypeMap = 
cmd.getServiceOfferingNodeTypeMap();
 
         if (name == null || name.isEmpty()) {
             throw new InvalidParameterValueException("Invalid name for the 
Kubernetes cluster name: " + name);
@@ -804,10 +809,7 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
             throw new InvalidParameterValueException(String.format("ISO 
associated with version ID: %s is not in Ready state for datacenter ID: %s",  
clusterKubernetesVersion.getUuid(), zone.getUuid()));
         }
 
-        ServiceOffering serviceOffering = 
serviceOfferingDao.findById(serviceOfferingId);
-        if (serviceOffering == null) {
-            throw new InvalidParameterValueException("No service offering with 
ID: " + serviceOfferingId);
-        }
+        validateServiceOfferingsForNodeTypes(serviceOfferingNodeTypeMap, 
cmd.getEtcdNodes(), clusterKubernetesVersion);
 
         validateSshKeyPairForKubernetesCreateParameters(sshKeyPair, owner);
 
@@ -815,10 +817,6 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
             throw new InvalidParameterValueException(String.format("Invalid 
value for %s", ApiConstants.NODE_ROOT_DISK_SIZE));
         }
 
-        if (!validateServiceOffering(serviceOffering, 
clusterKubernetesVersion)) {
-            throw new InvalidParameterValueException("Given service offering 
ID: %s is not suitable for Kubernetes cluster");
-        }
-
         validateDockerRegistryParams(dockerRegistryUserName, 
dockerRegistryPassword, dockerRegistryUrl);
 
         Network network = 
validateAndGetNetworkForKubernetesCreateParameters(networkId);
@@ -840,6 +838,40 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         }
     }
 
+    protected void validateServiceOfferingsForNodeTypes(Map<String, Long> map, 
Long etcdNodes, KubernetesSupportedVersion clusterKubernetesVersion) {
+        if (MapUtils.isEmpty(map)) {
+            throw new InvalidParameterValueException("Please specify at least 
one service offering for the cluster");
+        }
+        if (!map.containsKey(WORKER.name()) || 
!map.containsKey(CONTROL.name())) {
+            throw new InvalidParameterValueException("Please specify a service 
offering for worker and control nodes");
+        }
+        if (etcdNodes != null && etcdNodes > 0 && 
!map.containsKey(ETCD.name())) {
+            throw new InvalidParameterValueException("Please specify a service 
offering for the etcd nodes");
+        }
+        for (String key : CLUSTER_NODES_TYPES_LIST) {
+            validateServiceOfferingForNode(map, key, etcdNodes, 
clusterKubernetesVersion);
+        }
+    }
+
+    protected void validateServiceOfferingForNode(Map<String, Long> map, 
String key, Long etcdNodes, KubernetesSupportedVersion 
clusterKubernetesVersion) {
+        if (ETCD.name().equalsIgnoreCase(key) && (etcdNodes == null || 
etcdNodes == 0)) {
+            return;
+        }
+        Long serviceOfferingId = map.getOrDefault(key, null);
+        ServiceOffering serviceOffering = serviceOfferingId != null ? 
serviceOfferingDao.findById(serviceOfferingId) : null;
+        if (serviceOffering == null) {
+            throw new InvalidParameterValueException("No service offering 
found with ID: " + serviceOfferingId);
+        }
+        try {
+            validateServiceOffering(serviceOffering, clusterKubernetesVersion);
+        } catch (InvalidParameterValueException e) {
+            String msg = String.format("Given service offering ID: %s for %s 
nodes is not suitable for the Kubernetes cluster version %s - %s",
+                    serviceOffering, key, clusterKubernetesVersion, 
e.getMessage());
+            LOGGER.error(msg);
+            throw new InvalidParameterValueException(msg);
+        }
+    }
+
     private Network getKubernetesClusterNetworkIfMissing(final String 
clusterName, final DataCenter zone,  final Account owner, final int 
controlNodesCount,
                          final int nodesCount, final String 
externalLoadBalancerIpAddress, final Long networkId) throws 
CloudRuntimeException {
         Network network = null;
@@ -1154,6 +1186,7 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         final long controlNodeCount = cmd.getControlNodes();
         final long clusterSize = 
Objects.requireNonNullElse(cmd.getClusterSize(), 0L);
         final ServiceOffering serviceOffering = 
serviceOfferingDao.findById(cmd.getServiceOfferingId());
+        Map<String, Long> nodeTypeOfferingMap = 
cmd.getServiceOfferingNodeTypeMap();
         final Account owner = 
accountService.getActiveAccountById(cmd.getEntityOwnerId());
         final KubernetesSupportedVersion clusterKubernetesVersion = 
kubernetesSupportedVersionDao.findById(cmd.getKubernetesVersionId());
 
@@ -1203,20 +1236,14 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         final DataCenter zone = dataCenterDao.findById(cmd.getZoneId());
         final long controlNodeCount = cmd.getControlNodes();
         final long clusterSize = cmd.getClusterSize();
-        final long totalNodeCount = controlNodeCount + clusterSize;
-        final ServiceOffering serviceOffering = 
serviceOfferingDao.findById(cmd.getServiceOfferingId());
+        final long etcdNodes = cmd.getEtcdNodes();
+        final Map<String, Long> nodeTypeCount = Map.of(WORKER.name(), 
clusterSize,
+                CONTROL.name(), controlNodeCount, ETCD.name(), etcdNodes);
         final Account owner = 
accountService.getActiveAccountById(cmd.getEntityOwnerId());
         final KubernetesSupportedVersion clusterKubernetesVersion = 
kubernetesSupportedVersionDao.findById(cmd.getKubernetesVersionId());
 
-        DeployDestination deployDestination = null;
-        try {
-            deployDestination = plan(totalNodeCount, zone, serviceOffering);
-        } catch (InsufficientCapacityException e) {
-            logAndThrow(Level.ERROR, String.format("Creating Kubernetes 
cluster failed due to insufficient capacity for %d nodes cluster in zone : %s 
with service offering : %s", totalNodeCount, zone.getName(), 
serviceOffering.getName()));
-        }
-        if (deployDestination == null || deployDestination.getCluster() == 
null) {
-            logAndThrow(Level.ERROR, String.format("Creating Kubernetes 
cluster failed due to error while finding suitable deployment plan for cluster 
in zone : %s", zone.getName()));
-        }
+        Map<String, Long> serviceOfferingNodeTypeMap = 
cmd.getServiceOfferingNodeTypeMap();
+        Hypervisor.HypervisorType hypervisorType = 
validateDeploymentAndSelectDestinationHypervisor(serviceOfferingNodeTypeMap, 
nodeTypeCount, zone);
 
         SecurityGroup securityGroup = null;
         if (zone.isSecurityGroupEnabled()) {
@@ -1224,9 +1251,12 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         }
 
         final Network defaultNetwork = 
getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, 
(int)controlNodeCount, (int)clusterSize, 
cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId());
-        final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, 
deployDestination.getCluster().getHypervisorType());
-        final long cores = serviceOffering.getCpu() * (controlNodeCount + 
clusterSize);
-        final long memory = serviceOffering.getRamSize() * (controlNodeCount + 
clusterSize);
+        final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, 
hypervisorType);
+        // Set the service_offering_id as the ID of the worker nodes offering 
for backwards compatibility
+        final ServiceOffering serviceOffering = 
serviceOfferingDao.findById(serviceOfferingNodeTypeMap.get(WORKER.name()));
+        Pair<Long, Long> capacityPair = 
calculateClusterCapacity(serviceOfferingNodeTypeMap, nodeTypeCount);
+        final long cores = capacityPair.first();
+        final long memory = capacityPair.second();
 
         final SecurityGroup finalSecurityGroup = securityGroup;
         final KubernetesClusterVO cluster = Transaction.execute(new 
TransactionCallback<KubernetesClusterVO>() {
@@ -1236,6 +1266,12 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
                         serviceOffering.getId(), finalTemplate.getId(), 
defaultNetwork.getId(), owner.getDomainId(),
                         owner.getAccountId(), controlNodeCount, clusterSize, 
KubernetesCluster.State.Created, cmd.getSSHKeyPairName(), cores, memory,
                         cmd.getNodeRootDiskSize(), "", 
KubernetesCluster.ClusterType.CloudManaged);
+                
newCluster.setWorkerServiceOfferingId(serviceOfferingNodeTypeMap.getOrDefault(WORKER.name(),
 null));
+                
newCluster.setControlServiceOfferingId(serviceOfferingNodeTypeMap.getOrDefault(CONTROL.name(),
 null));
+                if (etcdNodes > 0) {
+                    newCluster.setEtcdNodeCount(etcdNodes);
+                    
newCluster.setEtcdServiceOfferingId(serviceOfferingNodeTypeMap.getOrDefault(ETCD.name(),
 null));
+                }
                 if (zone.isSecurityGroupEnabled()) {
                     newCluster.setSecurityGroupId(finalSecurityGroup.getId());
                 }
@@ -1252,6 +1288,49 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         return cluster;
     }
 
+    protected Pair<Long, Long> calculateClusterCapacity(Map<String, Long> map, 
Map<String, Long> nodeTypeCount) {
+        long cores = 0L;
+        long memory = 0L;
+        for (String key : CLUSTER_NODES_TYPES_LIST) {
+            if (!map.containsKey(key)) {
+                continue;
+            }
+            ServiceOffering serviceOffering = 
serviceOfferingDao.findById(map.get(key));
+            Long nodes = nodeTypeCount.get(key);
+            cores = cores + (serviceOffering.getCpu() * nodes);
+            memory = memory + (serviceOffering.getRamSize() * nodes);
+        }
+        return new Pair<>(cores, memory);
+    }
+
+    protected Hypervisor.HypervisorType 
validateDeploymentAndSelectDestinationHypervisor(Map<String, Long> 
serviceOfferingNodeTypeMap,
+                                                                               
          Map<String, Long> nodeTypeCount, DataCenter zone) {
+        Hypervisor.HypervisorType hypervisorType = null;
+        List<ServiceOffering> serviceOfferingList = new ArrayList<>();
+        for (String nodeType : CLUSTER_NODES_TYPES_LIST) {
+            ServiceOffering serviceOffering = null;
+            Long nodes = nodeTypeCount.get(nodeType);
+            try {
+                if (nodeType.equalsIgnoreCase(ETCD.name()) &&
+                        (!serviceOfferingNodeTypeMap.containsKey(ETCD.name()) 
|| nodes == 0)) {
+                    continue;
+                }
+                serviceOffering = 
serviceOfferingDao.findById(serviceOfferingNodeTypeMap.get(nodeType));
+                DeployDestination deployDestination = plan(nodes, zone, 
serviceOffering);
+                if (deployDestination == null || 
deployDestination.getCluster() == null) {
+                    logAndThrow(Level.ERROR, String.format("Creating 
Kubernetes cluster failed due to error while finding suitable deployment plan 
for cluster in zone : %s", zone.getName()));
+                }
+                if (hypervisorType == null) {
+                    hypervisorType = 
deployDestination.getCluster().getHypervisorType();
+                }
+                serviceOfferingList.add(serviceOffering);
+            } catch (InsufficientCapacityException e) {
+                logAndThrow(Level.ERROR, String.format("Creating Kubernetes 
cluster failed due to insufficient capacity for %d nodes cluster in zone : %s 
with service offering : %s", nodes, zone.getName(), serviceOffering.getName()));
+            }
+        }
+        return hypervisorType;
+    }
+
     private SecurityGroup getOrCreateSecurityGroupForAccount(Account owner) {
         String securityGroupName = String.format("%s-%s", 
KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME, owner.getUuid());
         String securityGroupDesc = String.format("%s and account %s", 
KubernetesClusterActionWorker.CKS_SECURITY_GROUP_DESCRIPTION, owner.getName());
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
index 270916aab7e..deeca36e389 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
@@ -117,6 +117,18 @@ public class KubernetesClusterVO implements 
KubernetesCluster {
     @Column(name = "cluster_type")
     private ClusterType clusterType;
 
+    @Column(name = "control_service_offering_id")
+    private Long controlServiceOfferingId;
+
+    @Column(name = "worker_service_offering_id")
+    private Long workerServiceOfferingId;
+
+    @Column(name = "etcd_service_offering_id")
+    private Long etcdServiceOfferingId;
+
+    @Column(name = "etcd_node_count")
+    private Long etcdNodeCount;
+
     @Override
     public long getId() {
         return id;
@@ -406,4 +418,36 @@ public class KubernetesClusterVO implements 
KubernetesCluster {
     public Class<?> getEntityType() {
         return KubernetesCluster.class;
     }
+
+    public Long getControlServiceOfferingId() {
+        return controlServiceOfferingId;
+    }
+
+    public void setControlServiceOfferingId(Long controlServiceOfferingId) {
+        this.controlServiceOfferingId = controlServiceOfferingId;
+    }
+
+    public Long getWorkerServiceOfferingId() {
+        return workerServiceOfferingId;
+    }
+
+    public void setWorkerServiceOfferingId(Long workerServiceOfferingId) {
+        this.workerServiceOfferingId = workerServiceOfferingId;
+    }
+
+    public Long getEtcdServiceOfferingId() {
+        return etcdServiceOfferingId;
+    }
+
+    public void setEtcdServiceOfferingId(Long etcdServiceOfferingId) {
+        this.etcdServiceOfferingId = etcdServiceOfferingId;
+    }
+
+    public Long getEtcdNodeCount() {
+        return etcdNodeCount;
+    }
+
+    public void setEtcdNodeCount(Long etcdNodeCount) {
+        this.etcdNodeCount = etcdNodeCount;
+    }
 }
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
 
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
index 99943408fc6..b950f071bce 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
@@ -101,7 +101,7 @@ public class CreateKubernetesClusterCmd extends 
BaseAsyncCreateCmd {
     @ACL(accessType = AccessType.UseEntry)
     @Parameter(name = ApiConstants.NODE_TYPE_OFFERING_MAP, type = 
CommandType.MAP,
             description = "(Optional) Node Type to Service Offering ID 
mapping. If provided, it overrides the serviceofferingid parameter")
-    protected Map<String, Map<String, String>> nodeTypeOfferingMap;
+    protected Map<String, Map<String, String>> serviceOfferingNodeTypeMap;
 
     @ACL(accessType = AccessType.UseEntry)
     @Parameter(name = ApiConstants.ETCD_NODES, type = CommandType.LONG,
@@ -228,8 +228,8 @@ public class CreateKubernetesClusterCmd extends 
BaseAsyncCreateCmd {
         return controlNodes;
     }
 
-    public Long getEtcdNodes() {
-        return etcdNodes == null ? 0L : etcdNodes;
+    public long getEtcdNodes() {
+        return etcdNodes == null ? 0 : etcdNodes;
     }
 
     public String getExternalLoadBalancerIpAddress() {
@@ -313,10 +313,10 @@ public class CreateKubernetesClusterCmd extends 
BaseAsyncCreateCmd {
         addNodeTypeOfferingEntry(nodeTypeStr, serviceOfferingUuid, 
serviceOffering, mapping);
     }
 
-    public Map<String, Long> getNodeTypeOfferingMap() {
+    public Map<String, Long> getServiceOfferingNodeTypeMap() {
         Map<String, Long> mapping = new HashMap<>();
-        if (MapUtils.isNotEmpty(nodeTypeOfferingMap)) {
-            for (Map<String, String> entry : nodeTypeOfferingMap.values()) {
+        if (MapUtils.isNotEmpty(serviceOfferingNodeTypeMap)) {
+            for (Map<String, String> entry : 
serviceOfferingNodeTypeMap.values()) {
                 processNodeTypeOfferingEntryAndAddToMappingIfValid(entry, 
mapping);
             }
             addMissingNodeTypeDefaultOffering(mapping, serviceOfferingId, 
etcdNodes);
diff --git 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
index a6d46ffc9aa..e5eafc03512 100644
--- 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
+++ 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
@@ -27,16 +27,20 @@ import com.cloud.exception.PermissionDeniedException;
 import 
com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterActionWorker;
 import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
 import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
+import com.cloud.kubernetes.version.KubernetesSupportedVersion;
 import com.cloud.network.Network;
 import com.cloud.network.dao.FirewallRulesDao;
 import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.FirewallRuleVO;
 import com.cloud.network.vpc.NetworkACL;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.User;
+import com.cloud.utils.Pair;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.api.BaseCmd;
@@ -59,7 +63,12 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
+import static 
com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.CONTROL;
+import static 
com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.WORKER;
 
 @RunWith(MockitoJUnitRunner.class)
 public class KubernetesClusterManagerImplTest {
@@ -85,6 +94,9 @@ public class KubernetesClusterManagerImplTest {
     @Mock
     private AccountManager accountManager;
 
+    @Mock
+    private ServiceOfferingDao serviceOfferingDao;
+
     @Spy
     @InjectMocks
     KubernetesClusterManagerImpl kubernetesClusterManager;
@@ -292,4 +304,63 @@ public class KubernetesClusterManagerImplTest {
         
Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
         
Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 
0);
     }
+
+    @Test
+    public void testValidateServiceOfferingNodeType() {
+        Map<String, Long> map = new HashMap<>();
+        map.put(WORKER.name(), 1L);
+        map.put(CONTROL.name(), 2L);
+        ServiceOfferingVO serviceOffering = 
Mockito.mock(ServiceOfferingVO.class);
+        
Mockito.when(serviceOfferingDao.findById(1L)).thenReturn(serviceOffering);
+        Mockito.when(serviceOffering.isDynamic()).thenReturn(false);
+        Mockito.when(serviceOffering.getCpu()).thenReturn(2);
+        Mockito.when(serviceOffering.getRamSize()).thenReturn(2048);
+        KubernetesSupportedVersion version = 
Mockito.mock(KubernetesSupportedVersion.class);
+        Mockito.when(version.getMinimumCpu()).thenReturn(2);
+        Mockito.when(version.getMinimumRamSize()).thenReturn(2048);
+        kubernetesClusterManager.validateServiceOfferingForNode(map, 
WORKER.name(), null, version);
+        
Mockito.verify(kubernetesClusterManager).validateServiceOffering(serviceOffering,
 version);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testValidateServiceOfferingNodeTypeInvalidOffering() {
+        Map<String, Long> map = new HashMap<>();
+        map.put(WORKER.name(), 1L);
+        map.put(CONTROL.name(), 2L);
+        ServiceOfferingVO serviceOffering = 
Mockito.mock(ServiceOfferingVO.class);
+        
Mockito.when(serviceOfferingDao.findById(1L)).thenReturn(serviceOffering);
+        Mockito.when(serviceOffering.isDynamic()).thenReturn(true);
+        kubernetesClusterManager.validateServiceOfferingForNode(map, 
WORKER.name(), null, null);
+    }
+
+    @Test
+    public void testClusterCapacity() {
+        long workerOfferingId = 1L;
+        long controlOfferingId = 2L;
+        long workerCount = 2L;
+        long controlCount = 2L;
+
+        int workerOfferingCpus = 4;
+        int workerOfferingMemory = 4096;
+        int controlOfferingCpus = 2;
+        int controlOfferingMemory = 2048;
+
+        Map<String, Long> map = Map.of(WORKER.name(), workerOfferingId, 
CONTROL.name(), controlOfferingId);
+        Map<String, Long> nodeCount = Map.of(WORKER.name(), workerCount, 
CONTROL.name(), controlCount);
+
+        ServiceOfferingVO workerOffering = 
Mockito.mock(ServiceOfferingVO.class);
+        
Mockito.when(serviceOfferingDao.findById(workerOfferingId)).thenReturn(workerOffering);
+        ServiceOfferingVO controlOffering = 
Mockito.mock(ServiceOfferingVO.class);
+        
Mockito.when(serviceOfferingDao.findById(controlOfferingId)).thenReturn(controlOffering);
+        Mockito.when(workerOffering.getCpu()).thenReturn(workerOfferingCpus);
+        
Mockito.when(workerOffering.getRamSize()).thenReturn(workerOfferingMemory);
+        Mockito.when(controlOffering.getCpu()).thenReturn(controlOfferingCpus);
+        
Mockito.when(controlOffering.getRamSize()).thenReturn(controlOfferingMemory);
+
+        Pair<Long, Long> pair = 
kubernetesClusterManager.calculateClusterCapacity(map, nodeCount);
+        Long expectedCpu = (workerOfferingCpus * workerCount) + 
(controlOfferingCpus * controlCount);
+        Long expectedMemory = (workerOfferingMemory * workerCount) + 
(controlOfferingMemory * controlCount);
+        Assert.assertEquals(expectedCpu, pair.first());
+        Assert.assertEquals(expectedMemory, pair.second());
+    }
 }
diff --git 
a/plugins/integrations/kubernetes-service/src/test/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmdTest.java
 
b/plugins/integrations/kubernetes-service/src/test/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmdTest.java
index 406516581a7..6dbbc53ec22 100644
--- 
a/plugins/integrations/kubernetes-service/src/test/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmdTest.java
+++ 
b/plugins/integrations/kubernetes-service/src/test/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmdTest.java
@@ -83,12 +83,12 @@ public class CreateKubernetesClusterCmdTest {
 
     @Test
     public void testNodeOfferingMapMissingEtcd() {
-        cmd.nodeTypeOfferingMap = new HashMap<>();
+        cmd.serviceOfferingNodeTypeMap = new HashMap<>();
         Map<String, String> firstMap = createMapEntry(WORKER, 
workerNodesOfferingId);
         Map<String, String> secondMap = createMapEntry(CONTROL, 
controlNodesOfferingId);
-        cmd.nodeTypeOfferingMap.put("map1", firstMap);
-        cmd.nodeTypeOfferingMap.put("map2", secondMap);
-        Map<String, Long> map = cmd.getNodeTypeOfferingMap();
+        cmd.serviceOfferingNodeTypeMap.put("map1", firstMap);
+        cmd.serviceOfferingNodeTypeMap.put("map2", secondMap);
+        Map<String, Long> map = cmd.getServiceOfferingNodeTypeMap();
         Assert.assertNotNull(map);
         Assert.assertEquals(2, map.size());
         Assert.assertTrue(map.containsKey(WORKER.name()) && 
map.containsKey(CONTROL.name()));
@@ -98,9 +98,9 @@ public class CreateKubernetesClusterCmdTest {
 
     @Test
     public void testNodeOfferingMapNullMap() {
-        cmd.nodeTypeOfferingMap = null;
+        cmd.serviceOfferingNodeTypeMap = null;
         cmd.serviceOfferingId = controlOfferingId;
-        Map<String, Long> map = cmd.getNodeTypeOfferingMap();
+        Map<String, Long> map = cmd.getServiceOfferingNodeTypeMap();
         Assert.assertNotNull(map);
         Assert.assertEquals(2, map.size());
         Assert.assertTrue(map.containsKey(WORKER.name()) && 
map.containsKey(CONTROL.name()));
@@ -110,12 +110,12 @@ public class CreateKubernetesClusterCmdTest {
 
     @Test
     public void testNodeOfferingMapEtcdNodes() {
-        cmd.nodeTypeOfferingMap = new HashMap<>();
+        cmd.serviceOfferingNodeTypeMap = new HashMap<>();
         Map<String, String> firstMap = createMapEntry(ETCD, 
etcdNodesOfferingId);
-        cmd.nodeTypeOfferingMap.put("map1", firstMap);
+        cmd.serviceOfferingNodeTypeMap.put("map1", firstMap);
         cmd.etcdNodes = 2L;
         cmd.serviceOfferingId = controlOfferingId;
-        Map<String, Long> map = cmd.getNodeTypeOfferingMap();
+        Map<String, Long> map = cmd.getServiceOfferingNodeTypeMap();
         Assert.assertNotNull(map);
         Assert.assertEquals(3, map.size());
         Assert.assertTrue(map.containsKey(WORKER.name()) && 
map.containsKey(CONTROL.name()) && map.containsKey(ETCD.name()));

Reply via email to