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