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 2cf8d674a833ffb7064a05e94874ddab5d7e9dbc Author: nvazquez <nicovazque...@gmail.com> AuthorDate: Thu Feb 8 00:39:42 2024 -0300 In progress scale kubernetes cluster --- .../cluster/KubernetesClusterHelper.java | 5 +- .../cluster/KubernetesClusterHelperImpl.java | 66 ++++++++++ .../cluster/KubernetesClusterManagerImpl.java | 97 ++++++++++----- .../KubernetesClusterScaleWorker.java | 4 + .../cluster/CreateKubernetesClusterCmd.java | 56 +-------- .../cluster/ScaleKubernetesClusterCmd.java | 13 ++ .../cluster/KubernetesClusterHelperImplTest.java | 100 +++++++++++++++ .../cluster/KubernetesClusterManagerImplTest.java | 51 ++++++++ .../cluster/CreateKubernetesClusterCmdTest.java | 136 --------------------- ui/src/views/compute/ScaleKubernetesCluster.vue | 130 +++++++++++++++++--- 10 files changed, 418 insertions(+), 240 deletions(-) diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java index 4b61fe5ad03..548a016c1c5 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java @@ -19,13 +19,16 @@ package com.cloud.kubernetes.cluster; import com.cloud.utils.component.Adapter; import org.apache.cloudstack.acl.ControlledEntity; +import java.util.Map; + public interface KubernetesClusterHelper extends Adapter { enum KubernetesClusterNodeType { - CONTROL, WORKER, ETCD + CONTROL, WORKER, ETCD, ALL } ControlledEntity findByUuid(String uuid); ControlledEntity findByVmId(long vmId); boolean isValidNodeType(String nodeType); + Map<String, Long> getServiceOfferingNodeTypeMap(Map<String, Map<String, String>> serviceOfferingNodeTypeMap); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java index 639e17bd560..43802c91e41 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java @@ -16,25 +16,37 @@ // under the License. package com.cloud.kubernetes.cluster; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao; import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao; +import com.cloud.offering.ServiceOffering; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.utils.component.AdapterBase; +import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; @Component public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable { + public static final Logger LOGGER = Logger.getLogger(KubernetesClusterHelperImpl.class.getName()); + @Inject private KubernetesClusterDao kubernetesClusterDao; @Inject private KubernetesClusterVmMapDao kubernetesClusterVmMapDao; + @Inject + protected ServiceOfferingDao serviceOfferingDao; @Override public ControlledEntity findByUuid(String uuid) { @@ -63,6 +75,60 @@ public class KubernetesClusterHelperImpl extends AdapterBase implements Kubernet } } + protected void checkNodeTypeOfferingEntryCompleteness(String nodeTypeStr, String serviceOfferingUuid) { + if (StringUtils.isAnyEmpty(nodeTypeStr, serviceOfferingUuid)) { + String error = String.format("Incomplete Node Type to Service Offering ID mapping: '%s' -> '%s'", nodeTypeStr, serviceOfferingUuid); + LOGGER.error(error); + throw new InvalidParameterValueException(error); + } + } + + protected void checkNodeTypeOfferingEntryValues(String nodeTypeStr, ServiceOffering serviceOffering, String serviceOfferingUuid) { + if (!isValidNodeType(nodeTypeStr)) { + String error = String.format("The provided value '%s' for Node Type is invalid", nodeTypeStr); + LOGGER.error(error); + throw new InvalidParameterValueException(String.format(error)); + } + if (serviceOffering == null) { + String error = String.format("Cannot find a service offering with ID %s", serviceOfferingUuid); + LOGGER.error(error); + throw new InvalidParameterValueException(error); + } + } + + protected void addNodeTypeOfferingEntry(String nodeTypeStr, String serviceOfferingUuid, ServiceOffering serviceOffering, Map<String, Long> mapping) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("Node Type: '%s' should use Service Offering ID: '%s'", nodeTypeStr, serviceOfferingUuid)); + } + KubernetesClusterNodeType nodeType = KubernetesClusterNodeType.valueOf(nodeTypeStr.toUpperCase()); + mapping.put(nodeType.name(), serviceOffering.getId()); + } + + protected void processNodeTypeOfferingEntryAndAddToMappingIfValid(Map<String, String> entry, Map<String, Long> mapping) { + if (MapUtils.isEmpty(entry)) { + return; + } + String nodeTypeStr = entry.get(VmDetailConstants.CKS_NODE_TYPE); + String serviceOfferingUuid = entry.get(VmDetailConstants.OFFERING); + checkNodeTypeOfferingEntryCompleteness(nodeTypeStr, serviceOfferingUuid); + + ServiceOffering serviceOffering = serviceOfferingDao.findByUuid(serviceOfferingUuid); + checkNodeTypeOfferingEntryValues(nodeTypeStr, serviceOffering, serviceOfferingUuid); + + addNodeTypeOfferingEntry(nodeTypeStr, serviceOfferingUuid, serviceOffering, mapping); + } + + @Override + public Map<String, Long> getServiceOfferingNodeTypeMap(Map<String, Map<String, String>> serviceOfferingNodeTypeMap) { + Map<String, Long> mapping = new HashMap<>(); + if (MapUtils.isNotEmpty(serviceOfferingNodeTypeMap)) { + for (Map<String, String> entry : serviceOfferingNodeTypeMap.values()) { + processNodeTypeOfferingEntryAndAddToMappingIfValid(entry, mapping); + } + } + return mapping; + } + @Override public String getConfigComponentName() { return KubernetesClusterHelper.class.getSimpleName(); 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 93dd48b34ae..47cedd996a2 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,7 @@ // under the License. package com.cloud.kubernetes.cluster; +import static com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.ALL; 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; @@ -76,6 +77,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; @@ -1008,12 +1010,13 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne private void validateKubernetesClusterScaleParameters(ScaleKubernetesClusterCmd cmd) { final Long kubernetesClusterId = cmd.getId(); - final Long serviceOfferingId = cmd.getServiceOfferingId(); final Long clusterSize = cmd.getClusterSize(); final List<Long> nodeIds = cmd.getNodeIds(); final Boolean isAutoscalingEnabled = cmd.isAutoscalingEnabled(); final Long minSize = cmd.getMinSize(); final Long maxSize = cmd.getMaxSize(); + final Long defaultServiceOfferingId = cmd.getServiceOfferingId(); + final Map<String, Long> serviceOfferingNodeTypeMap = cmd.getServiceOfferingNodeTypeMap(); if (kubernetesClusterId == null || kubernetesClusterId < 1L) { throw new InvalidParameterValueException("Invalid Kubernetes cluster ID"); @@ -1029,7 +1032,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logAndThrow(Level.WARN, String.format("Unable to find zone for Kubernetes cluster : %s", kubernetesCluster.getName())); } - if (serviceOfferingId == null && clusterSize == null && nodeIds == null && isAutoscalingEnabled == null) { + if (defaultServiceOfferingId == null && isAnyNodeOfferingEmpty(serviceOfferingNodeTypeMap) + && clusterSize == null && nodeIds == null && isAutoscalingEnabled == null) { throw new InvalidParameterValueException(String.format("Kubernetes cluster %s cannot be scaled, either service offering or cluster size or nodeids to be removed or autoscaling must be passed", kubernetesCluster.getName())); } @@ -1076,8 +1080,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } } + Long workerOfferingId = serviceOfferingNodeTypeMap != null ? serviceOfferingNodeTypeMap.getOrDefault(WORKER.name(), null) : null; if (nodeIds != null) { - if (clusterSize != null || serviceOfferingId != null) { + if (clusterSize != null || defaultServiceOfferingId != null || workerOfferingId != null) { throw new InvalidParameterValueException("nodeids can not be passed along with clustersize or service offering"); } List<KubernetesClusterVmMapVO> nodes = kubernetesClusterVmMapDao.listByClusterIdAndVmIdsIn(kubernetesCluster.getId(), nodeIds); @@ -1097,37 +1102,53 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne } } - ServiceOffering serviceOffering = null; - if (serviceOfferingId != null) { - serviceOffering = serviceOfferingDao.findById(serviceOfferingId); - if (serviceOffering == null) { - throw new InvalidParameterValueException("Failed to find service offering ID: " + serviceOfferingId); - } else { - if (serviceOffering.isDynamic()) { - throw new InvalidParameterValueException(String.format("Custom service offerings are not supported for Kubernetes clusters. Kubernetes cluster : %s, service offering : %s", kubernetesCluster.getName(), serviceOffering.getName())); - } - if (serviceOffering.getCpu() < MIN_KUBERNETES_CLUSTER_NODE_CPU || serviceOffering.getRamSize() < MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE) { - throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, Kubernetes cluster template(CoreOS) needs minimum %d vCPUs and %d MB RAM", - kubernetesCluster.getName(), serviceOffering.getName(), MIN_KUBERNETES_CLUSTER_NODE_CPU, MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE)); - } - if (serviceOffering.getCpu() < clusterVersion.getMinimumCpu()) { - throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, associated Kubernetes version : %s needs minimum %d vCPUs", - kubernetesCluster.getName(), serviceOffering.getName(), clusterVersion.getName(), clusterVersion.getMinimumCpu())); + validateServiceOfferingsForNodeTypesScale(serviceOfferingNodeTypeMap, defaultServiceOfferingId, kubernetesCluster, clusterVersion); + + validateKubernetesClusterScaleSize(kubernetesCluster, clusterSize, maxClusterSize, zone); + } + + protected void validateServiceOfferingsForNodeTypesScale(Map<String, Long> map, Long defaultServiceOfferingId, KubernetesClusterVO kubernetesCluster, KubernetesSupportedVersion clusterVersion) { + for (String key : CLUSTER_NODES_TYPES_LIST) { + Long serviceOfferingId = map.getOrDefault(key, defaultServiceOfferingId); + if (serviceOfferingId != null) { + ServiceOffering serviceOffering = serviceOfferingDao.findById(serviceOfferingId); + if (serviceOffering == null) { + throw new InvalidParameterValueException("Failed to find service offering ID: " + serviceOfferingId); } - if (serviceOffering.getRamSize() < clusterVersion.getMinimumRamSize()) { - throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, associated Kubernetes version : %s needs minimum %d MB RAM", - kubernetesCluster.getName(), serviceOffering.getName(), clusterVersion.getName(), clusterVersion.getMinimumRamSize())); + checkServiceOfferingForNodesScale(serviceOffering, kubernetesCluster, clusterVersion); + final ServiceOffering existingServiceOffering = serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId()); + if (KubernetesCluster.State.Running.equals(kubernetesCluster.getState()) && (serviceOffering.getRamSize() < existingServiceOffering.getRamSize() || + serviceOffering.getCpu() * serviceOffering.getSpeed() < existingServiceOffering.getCpu() * existingServiceOffering.getSpeed())) { + logAndThrow(Level.WARN, String.format("Kubernetes cluster cannot be scaled down for service offering. Service offering : %s offers lesser resources as compared to service offering : %s of Kubernetes cluster : %s", + serviceOffering.getName(), existingServiceOffering.getName(), kubernetesCluster.getName())); } } - final ServiceOffering existingServiceOffering = serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId()); - if (KubernetesCluster.State.Running.equals(kubernetesCluster.getState()) && (serviceOffering.getRamSize() < existingServiceOffering.getRamSize() || - serviceOffering.getCpu() * serviceOffering.getSpeed() < existingServiceOffering.getCpu() * existingServiceOffering.getSpeed())) { - logAndThrow(Level.WARN, String.format("Kubernetes cluster cannot be scaled down for service offering. Service offering : %s offers lesser resources as compared to service offering : %s of Kubernetes cluster : %s", - serviceOffering.getName(), existingServiceOffering.getName(), kubernetesCluster.getName())); - } } + } - validateKubernetesClusterScaleSize(kubernetesCluster, clusterSize, maxClusterSize, zone); + protected void checkServiceOfferingForNodesScale(ServiceOffering serviceOffering, KubernetesClusterVO kubernetesCluster, KubernetesSupportedVersion clusterVersion) { + if (serviceOffering.isDynamic()) { + throw new InvalidParameterValueException(String.format("Custom service offerings are not supported for Kubernetes clusters. Kubernetes cluster : %s, service offering : %s", kubernetesCluster.getName(), serviceOffering.getName())); + } + if (serviceOffering.getCpu() < MIN_KUBERNETES_CLUSTER_NODE_CPU || serviceOffering.getRamSize() < MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE) { + throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, Kubernetes cluster template(CoreOS) needs minimum %d vCPUs and %d MB RAM", + kubernetesCluster.getName(), serviceOffering.getName(), MIN_KUBERNETES_CLUSTER_NODE_CPU, MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE)); + } + if (serviceOffering.getCpu() < clusterVersion.getMinimumCpu()) { + throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, associated Kubernetes version : %s needs minimum %d vCPUs", + kubernetesCluster.getName(), serviceOffering.getName(), clusterVersion.getName(), clusterVersion.getMinimumCpu())); + } + if (serviceOffering.getRamSize() < clusterVersion.getMinimumRamSize()) { + throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s cannot be scaled with service offering : %s, associated Kubernetes version : %s needs minimum %d MB RAM", + kubernetesCluster.getName(), serviceOffering.getName(), clusterVersion.getName(), clusterVersion.getMinimumRamSize())); + } + } + + protected boolean isAnyNodeOfferingEmpty(Map<String, Long> map) { + if (MapUtils.isEmpty(map)) { + return false; + } + return map.values().stream().anyMatch(Objects::isNull); } private void validateKubernetesClusterUpgradeParameters(UpgradeKubernetesClusterCmd cmd) { @@ -1652,12 +1673,14 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled"); } validateKubernetesClusterScaleParameters(cmd); + Map<String, ServiceOffering> nodeToOfferingMap = createNodeTypeToServiceOfferingMap(cmd.getServiceOfferingNodeTypeMap(), cmd.getServiceOfferingId()); KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(cmd.getId()); String[] keys = getServiceUserKeys(kubernetesCluster); KubernetesClusterScaleWorker scaleWorker = new KubernetesClusterScaleWorker(kubernetesClusterDao.findById(cmd.getId()), serviceOfferingDao.findById(cmd.getServiceOfferingId()), + nodeToOfferingMap, cmd.getClusterSize(), cmd.getNodeIds(), cmd.isAutoscalingEnabled(), @@ -1669,6 +1692,22 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne return scaleWorker.scaleCluster(); } + protected Map<String, ServiceOffering> createNodeTypeToServiceOfferingMap(Map<String, Long> idsMapping, + Long serviceOfferingId) { + Map<String, ServiceOffering> map = new HashMap<>(); + if (MapUtils.isEmpty(idsMapping) && serviceOfferingId != null) { + map.put(ALL.name(), serviceOfferingDao.findById(serviceOfferingId)); + return map; + } + for (String key : CLUSTER_NODES_TYPES_LIST) { + if (!idsMapping.containsKey(key)) { + continue; + } + map.put(key, serviceOfferingDao.findById(idsMapping.get(key))); + } + return map; + } + @Override public boolean upgradeKubernetesCluster(UpgradeKubernetesClusterCmd cmd) throws CloudRuntimeException { if (!KubernetesServiceEnabled.value()) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java index df94642a881..1468b1cca5f 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.inject.Inject; @@ -64,6 +65,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif protected VMInstanceDao vmInstanceDao; private ServiceOffering serviceOffering; + private Map<String, ServiceOffering> serviceOfferingNodeTypeMap; private Long clusterSize; private List<Long> nodeIds; private KubernetesCluster.State originalState; @@ -75,6 +77,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif public KubernetesClusterScaleWorker(final KubernetesCluster kubernetesCluster, final ServiceOffering serviceOffering, + final Map<String, ServiceOffering> serviceOfferingNodeTypeMap, final Long clusterSize, final List<Long> nodeIds, final Boolean isAutoscalingEnabled, @@ -83,6 +86,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif final KubernetesClusterManagerImpl clusterManager) { super(kubernetesCluster, clusterManager); this.serviceOffering = serviceOffering; + this.serviceOfferingNodeTypeMap = serviceOfferingNodeTypeMap; this.nodeIds = nodeIds; this.isAutoscalingEnabled = isAutoscalingEnabled; this.minSize = minSize; 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 08ed1b1b280..466b55c0d2b 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 @@ -17,16 +17,12 @@ package org.apache.cloudstack.api.command.user.kubernetes.cluster; import java.security.InvalidParameterException; -import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import com.cloud.exception.InvalidParameterValueException; import com.cloud.kubernetes.cluster.KubernetesClusterHelper; -import com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType; -import com.cloud.offering.ServiceOffering; -import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; @@ -46,7 +42,6 @@ import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -268,57 +263,8 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd { return clusterType; } - protected void checkNodeTypeOfferingEntryCompleteness(String nodeTypeStr, String serviceOfferingUuid) { - if (StringUtils.isAnyEmpty(nodeTypeStr, serviceOfferingUuid)) { - String error = String.format("Incomplete Node Type to Service Offering ID mapping: '%s' -> '%s'", nodeTypeStr, serviceOfferingUuid); - LOGGER.error(error); - throw new InvalidParameterValueException(error); - } - } - - protected void checkNodeTypeOfferingEntryValues(String nodeTypeStr, ServiceOffering serviceOffering, String serviceOfferingUuid) { - if (!kubernetesClusterHelper.isValidNodeType(nodeTypeStr)) { - String error = String.format("The provided value '%s' for Node Type is invalid", nodeTypeStr); - LOGGER.error(error); - throw new InvalidParameterValueException(String.format(error)); - } - if (serviceOffering == null) { - String error = String.format("Cannot find a service offering with ID %s", serviceOfferingUuid); - LOGGER.error(error); - throw new InvalidParameterValueException(error); - } - } - - protected void addNodeTypeOfferingEntry(String nodeTypeStr, String serviceOfferingUuid, ServiceOffering serviceOffering, Map<String, Long> mapping) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(String.format("Node Type: '%s' should use Service Offering ID: '%s'", nodeTypeStr, serviceOfferingUuid)); - } - KubernetesClusterNodeType nodeType = KubernetesClusterNodeType.valueOf(nodeTypeStr.toUpperCase()); - mapping.put(nodeType.name(), serviceOffering.getId()); - } - - protected void processNodeTypeOfferingEntryAndAddToMappingIfValid(Map<String, String> entry, Map<String, Long> mapping) { - if (MapUtils.isEmpty(entry)) { - return; - } - String nodeTypeStr = entry.get(VmDetailConstants.CKS_NODE_TYPE); - String serviceOfferingUuid = entry.get(VmDetailConstants.OFFERING); - checkNodeTypeOfferingEntryCompleteness(nodeTypeStr, serviceOfferingUuid); - - ServiceOffering serviceOffering = _entityMgr.findByUuid(ServiceOffering.class, serviceOfferingUuid); - checkNodeTypeOfferingEntryValues(nodeTypeStr, serviceOffering, serviceOfferingUuid); - - addNodeTypeOfferingEntry(nodeTypeStr, serviceOfferingUuid, serviceOffering, mapping); - } - public Map<String, Long> getServiceOfferingNodeTypeMap() { - Map<String, Long> mapping = new HashMap<>(); - if (MapUtils.isNotEmpty(serviceOfferingNodeTypeMap)) { - for (Map<String, String> entry : serviceOfferingNodeTypeMap.values()) { - processNodeTypeOfferingEntryAndAddToMappingIfValid(entry, mapping); - } - } - return mapping; + return kubernetesClusterHelper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap); } ///////////////////////////////////////////////////// diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java index e5a5c902f4d..ade4684e1cd 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java +++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java @@ -17,9 +17,11 @@ package org.apache.cloudstack.api.command.user.kubernetes.cluster; import java.util.List; +import java.util.Map; import javax.inject.Inject; +import com.cloud.kubernetes.cluster.KubernetesClusterHelper; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ACL; @@ -55,6 +57,8 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd { @Inject public KubernetesClusterService kubernetesClusterService; + @Inject + protected KubernetesClusterHelper kubernetesClusterHelper; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -69,6 +73,11 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd { description = "the ID of the service offering for the virtual machines in the cluster.") private Long serviceOfferingId; + @ACL(accessType = SecurityChecker.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>> serviceOfferingNodeTypeMap; + @Parameter(name=ApiConstants.SIZE, type = CommandType.LONG, description = "number of Kubernetes cluster nodes") private Long clusterSize; @@ -104,6 +113,10 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd { return serviceOfferingId; } + public Map<String, Long> getServiceOfferingNodeTypeMap() { + return kubernetesClusterHelper.getServiceOfferingNodeTypeMap(this.serviceOfferingNodeTypeMap); + } + public Long getClusterSize() { return clusterSize; } diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java index bab58d20604..ba54ff38d38 100644 --- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java @@ -16,16 +16,58 @@ // under the License. package com.cloud.kubernetes.cluster; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.vm.VmDetailConstants; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +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; + @RunWith(MockitoJUnitRunner.class) public class KubernetesClusterHelperImplTest { + @Mock + private ServiceOfferingDao serviceOfferingDao; + @Mock + private ServiceOfferingVO workerServiceOffering; + @Mock + private ServiceOfferingVO controlServiceOffering; + @Mock + private ServiceOfferingVO etcdServiceOffering; + + private static final String workerNodesOfferingId = UUID.randomUUID().toString(); + private static final String controlNodesOfferingId = UUID.randomUUID().toString(); + private static final String etcdNodesOfferingId = UUID.randomUUID().toString(); + private static final Long workerOfferingId = 1L; + private static final Long controlOfferingId = 2L; + private static final Long etcdOfferingId = 3L; + private final KubernetesClusterHelperImpl helper = new KubernetesClusterHelperImpl(); + @Before + public void setUp() { + helper.serviceOfferingDao = serviceOfferingDao; + Mockito.when(serviceOfferingDao.findByUuid(workerNodesOfferingId)).thenReturn(workerServiceOffering); + Mockito.when(serviceOfferingDao.findByUuid(controlNodesOfferingId)).thenReturn(controlServiceOffering); + Mockito.when(serviceOfferingDao.findByUuid(etcdNodesOfferingId)).thenReturn(etcdServiceOffering); + Mockito.when(workerServiceOffering.getId()).thenReturn(workerOfferingId); + Mockito.when(controlServiceOffering.getId()).thenReturn(controlOfferingId); + Mockito.when(etcdServiceOffering.getId()).thenReturn(etcdOfferingId); + } + @Test public void testIsValidNodeTypeEmptyNodeType() { Assert.assertFalse(helper.isValidNodeType(null)); @@ -42,4 +84,62 @@ public class KubernetesClusterHelperImplTest { String nodeType = KubernetesClusterHelper.KubernetesClusterNodeType.WORKER.name().toLowerCase(); Assert.assertTrue(helper.isValidNodeType(nodeType)); } + + private Map<String, String> createMapEntry(KubernetesClusterHelper.KubernetesClusterNodeType nodeType, + String nodeTypeOfferingUuid) { + Map<String, String> map = new HashMap<>(); + map.put(VmDetailConstants.CKS_NODE_TYPE, nodeType.name().toLowerCase()); + map.put(VmDetailConstants.OFFERING, nodeTypeOfferingUuid); + return map; + } + + @Test + public void testNodeOfferingMap() { + Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>(); + Map<String, String> firstMap = createMapEntry(WORKER, workerNodesOfferingId); + Map<String, String> secondMap = createMapEntry(CONTROL, controlNodesOfferingId); + serviceOfferingNodeTypeMap.put("map1", firstMap); + serviceOfferingNodeTypeMap.put("map2", secondMap); + Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap); + Assert.assertNotNull(map); + Assert.assertEquals(2, map.size()); + Assert.assertTrue(map.containsKey(WORKER.name()) && map.containsKey(CONTROL.name())); + Assert.assertEquals(workerOfferingId, map.get(WORKER.name())); + Assert.assertEquals(controlOfferingId, map.get(CONTROL.name())); + } + + @Test + public void testNodeOfferingMapNullMap() { + Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(null); + Assert.assertTrue(map.isEmpty()); + } + + @Test + public void testNodeOfferingMapEtcdNodes() { + Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>(); + Map<String, String> firstMap = createMapEntry(ETCD, etcdNodesOfferingId); + serviceOfferingNodeTypeMap.put("map1", firstMap); + Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap); + Assert.assertNotNull(map); + Assert.assertEquals(1, map.size()); + Assert.assertTrue(map.containsKey(ETCD.name())); + Assert.assertEquals(etcdOfferingId, map.get(ETCD.name())); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckNodeTypeOfferingEntryCompletenessInvalidParameters() { + helper.checkNodeTypeOfferingEntryCompleteness(WORKER.name(), null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckNodeTypeOfferingEntryValuesInvalidNodeType() { + String invalidNodeType = "invalidNodeTypeName"; + helper.checkNodeTypeOfferingEntryValues(invalidNodeType, workerServiceOffering, workerNodesOfferingId); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckNodeTypeOfferingEntryValuesEmptyOffering() { + String nodeType = WORKER.name(); + helper.checkNodeTypeOfferingEntryValues(nodeType, null, workerNodesOfferingId); + } } 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 8ba91269244..49bddb969e4 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 @@ -33,6 +33,7 @@ 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.offering.ServiceOffering; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.VMTemplateVO; @@ -48,6 +49,7 @@ import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachi import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.commons.collections.MapUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -68,6 +70,7 @@ 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.ETCD; import static com.cloud.kubernetes.cluster.KubernetesClusterHelper.KubernetesClusterNodeType.WORKER; @RunWith(MockitoJUnitRunner.class) @@ -363,4 +366,52 @@ public class KubernetesClusterManagerImplTest { Assert.assertEquals(expectedCpu, pair.first()); Assert.assertEquals(expectedMemory, pair.second()); } + + @Test + public void testIsAnyNodeOfferingEmptyNullMap() { + Assert.assertFalse(kubernetesClusterManager.isAnyNodeOfferingEmpty(null)); + } + + @Test + public void testIsAnyNodeOfferingEmptyNullValue() { + Map<String, Long> map = new HashMap<>(); + map.put(WORKER.name(), 1L); + map.put(CONTROL.name(), null); + map.put(ETCD.name(), 2L); + Assert.assertTrue(kubernetesClusterManager.isAnyNodeOfferingEmpty(map)); + } + + @Test + public void testIsAnyNodeOfferingEmpty() { + Map<String, Long> map = new HashMap<>(); + map.put(WORKER.name(), 1L); + map.put(CONTROL.name(), 2L); + Assert.assertFalse(kubernetesClusterManager.isAnyNodeOfferingEmpty(map)); + } + + @Test + public void testCreateNodeTypeToServiceOfferingMapNullMap() { + Map<String, ServiceOffering> mapping = kubernetesClusterManager.createNodeTypeToServiceOfferingMap(null, null); + Assert.assertTrue(MapUtils.isEmpty(mapping)); + } + + @Test + public void testCreateNodeTypeToServiceOfferingMap() { + Map<String, Long> idsMap = new HashMap<>(); + long workerOfferingId = 1L; + long controlOfferingId = 2L; + idsMap.put(WORKER.name(), workerOfferingId); + idsMap.put(CONTROL.name(), controlOfferingId); + + 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); + + Map<String, ServiceOffering> mapping = kubernetesClusterManager.createNodeTypeToServiceOfferingMap(idsMap, null); + Assert.assertEquals(2, mapping.size()); + Assert.assertTrue(mapping.containsKey(WORKER.name()) && mapping.containsKey(CONTROL.name())); + Assert.assertEquals(workerOffering, mapping.get(WORKER.name())); + Assert.assertEquals(controlOffering, mapping.get(CONTROL.name())); + } } 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 deleted file mode 100644 index b5c6cf257a4..00000000000 --- a/plugins/integrations/kubernetes-service/src/test/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmdTest.java +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.command.user.kubernetes.cluster; - -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.kubernetes.cluster.KubernetesClusterHelper; -import com.cloud.kubernetes.cluster.KubernetesClusterHelperImpl; -import com.cloud.offering.ServiceOffering; -import com.cloud.utils.db.EntityManager; -import com.cloud.vm.VmDetailConstants; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -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; - -@RunWith(MockitoJUnitRunner.class) -public class CreateKubernetesClusterCmdTest { - - @Mock - EntityManager entityManager; - KubernetesClusterHelper helper = new KubernetesClusterHelperImpl(); - - @Mock - ServiceOffering workerServiceOffering; - @Mock - ServiceOffering controlServiceOffering; - @Mock - ServiceOffering etcdServiceOffering; - - private final CreateKubernetesClusterCmd cmd = new CreateKubernetesClusterCmd(); - - private static final String workerNodesOfferingId = UUID.randomUUID().toString(); - private static final String controlNodesOfferingId = UUID.randomUUID().toString(); - private static final String etcdNodesOfferingId = UUID.randomUUID().toString(); - private static final Long workerOfferingId = 1L; - private static final Long controlOfferingId = 2L; - private static final Long etcdOfferingId = 3L; - - @Before - public void setUp() { - cmd._entityMgr = entityManager; - cmd.kubernetesClusterHelper = helper; - Mockito.when(entityManager.findByUuid(ServiceOffering.class, workerNodesOfferingId)).thenReturn(workerServiceOffering); - Mockito.when(entityManager.findByUuid(ServiceOffering.class, controlNodesOfferingId)).thenReturn(controlServiceOffering); - Mockito.when(entityManager.findByUuid(ServiceOffering.class, etcdNodesOfferingId)).thenReturn(etcdServiceOffering); - Mockito.when(workerServiceOffering.getId()).thenReturn(workerOfferingId); - Mockito.when(controlServiceOffering.getId()).thenReturn(controlOfferingId); - Mockito.when(etcdServiceOffering.getId()).thenReturn(etcdOfferingId); - } - - private Map<String, String> createMapEntry(KubernetesClusterHelper.KubernetesClusterNodeType nodeType, - String nodeTypeOfferingUuid) { - Map<String, String> map = new HashMap<>(); - map.put(VmDetailConstants.CKS_NODE_TYPE, nodeType.name().toLowerCase()); - map.put(VmDetailConstants.OFFERING, nodeTypeOfferingUuid); - return map; - } - - @Test - public void testNodeOfferingMap() { - cmd.serviceOfferingNodeTypeMap = new HashMap<>(); - Map<String, String> firstMap = createMapEntry(WORKER, workerNodesOfferingId); - Map<String, String> secondMap = createMapEntry(CONTROL, controlNodesOfferingId); - 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())); - Assert.assertEquals(workerOfferingId, map.get(WORKER.name())); - Assert.assertEquals(controlOfferingId, map.get(CONTROL.name())); - } - - @Test - public void testNodeOfferingMapNullMap() { - cmd.serviceOfferingNodeTypeMap = null; - cmd.serviceOfferingId = controlOfferingId; - Map<String, Long> map = cmd.getServiceOfferingNodeTypeMap(); - Assert.assertTrue(map.isEmpty()); - } - - @Test - public void testNodeOfferingMapEtcdNodes() { - cmd.serviceOfferingNodeTypeMap = new HashMap<>(); - Map<String, String> firstMap = createMapEntry(ETCD, etcdNodesOfferingId); - cmd.serviceOfferingNodeTypeMap.put("map1", firstMap); - cmd.etcdNodes = 2L; - Map<String, Long> map = cmd.getServiceOfferingNodeTypeMap(); - Assert.assertNotNull(map); - Assert.assertEquals(1, map.size()); - Assert.assertTrue(map.containsKey(ETCD.name())); - Assert.assertEquals(etcdOfferingId, map.get(ETCD.name())); - } - - @Test(expected = InvalidParameterValueException.class) - public void testCheckNodeTypeOfferingEntryCompletenessInvalidParameters() { - cmd.checkNodeTypeOfferingEntryCompleteness(WORKER.name(), null); - } - - @Test(expected = InvalidParameterValueException.class) - public void testCheckNodeTypeOfferingEntryValuesInvalidNodeType() { - String invalidNodeType = "invalidNodeTypeName"; - cmd.checkNodeTypeOfferingEntryValues(invalidNodeType, workerServiceOffering, workerNodesOfferingId); - } - - @Test(expected = InvalidParameterValueException.class) - public void testCheckNodeTypeOfferingEntryValuesEmptyOffering() { - String nodeType = WORKER.name(); - cmd.checkNodeTypeOfferingEntryValues(nodeType, null, workerNodesOfferingId); - } -} diff --git a/ui/src/views/compute/ScaleKubernetesCluster.vue b/ui/src/views/compute/ScaleKubernetesCluster.vue index 8d73ac861d7..a0fdf4a470c 100644 --- a/ui/src/views/compute/ScaleKubernetesCluster.vue +++ b/ui/src/views/compute/ScaleKubernetesCluster.vue @@ -28,7 +28,7 @@ :rules="rules" @finish="handleSubmit" layout="vertical"> - <a-form-item name="serviceofferingid" ref="serviceofferingid"> + <a-form-item name="serviceofferingid" ref="serviceofferingid" v-if="!this.resource.workerofferingid && !this.resource.controlofferingid && !this.resource.etcdofferingid"> <template #label> <tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/> </template> @@ -47,6 +47,63 @@ </a-select-option> </a-select> </a-form-item> + <a-form-item name="workerofferingid" ref="workerofferingid"> + <template #label> + <tooltip-label :title="$t('label.service.offering.workernodes')" :tooltip="apiParams.serviceofferingid.description"/> + </template> + <a-select + id="offering-selection-worker" + v-model:value="form.workerofferingid" + showSearch + optionFilterProp="label" + :filterOption="(input, option) => { + return option.label.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }" + :loading="serviceOfferingLoading" + :placeholder="apiParams.serviceofferingid.description"> + <a-select-option v-for="(opt, optIndex) in workerOfferings" :key="optIndex" :label="opt.name || opt.description"> + {{ opt.name || opt.description }} + </a-select-option> + </a-select> + </a-form-item> + <a-form-item name="controlofferingid" ref="controlofferingid"> + <template #label> + <tooltip-label :title="$t('label.service.offering.controlnodes')" :tooltip="apiParams.serviceofferingid.description"/> + </template> + <a-select + id="offering-selection-control" + v-model:value="form.controlofferingid" + showSearch + optionFilterProp="label" + :filterOption="(input, option) => { + return option.label.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }" + :loading="serviceOfferingLoading" + :placeholder="apiParams.serviceofferingid.description"> + <a-select-option v-for="(opt, optIndex) in controlOfferings" :key="optIndex" :label="opt.name || opt.description"> + {{ opt.name || opt.description }} + </a-select-option> + </a-select> + </a-form-item> + <a-form-item name="etcdofferingid" ref="etcdofferingid" v-if="this.resource.etcdnodes && this.resource.etcdnodes > 0 && this.resource.etcdofferingid"> + <template #label> + <tooltip-label :title="$t('label.service.offering.etcdnodes')" :tooltip="apiParams.serviceofferingid.description"/> + </template> + <a-select + id="offering-selection-etcd" + v-model:value="form.etcdofferingid" + showSearch + optionFilterProp="label" + :filterOption="(input, option) => { + return option.label.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }" + :loading="serviceOfferingLoading" + :placeholder="apiParams.serviceofferingid.description"> + <a-select-option v-for="(opt, optIndex) in etcdOfferings" :key="optIndex" :label="opt.name || opt.description"> + {{ opt.name || opt.description }} + </a-select-option> + </a-select> + </a-form-item> <a-form-item name="autoscalingenabled" ref="autoscalingenabled" v-if="apiParams.autoscalingenabled"> <template #label> <tooltip-label :title="$t('label.cks.cluster.autoscalingenabled')" :tooltip="apiParams.autoscalingenabled.description"/> @@ -118,7 +175,10 @@ export default { originalSize: 1, autoscalingenabled: null, minsize: null, - maxsize: null + maxsize: null, + controlOfferings: [], + workerOfferings: [], + etcdOfferings: [] } }, beforeCreate () { @@ -153,7 +213,12 @@ export default { }, fetchData () { if (this.resource.state === 'Running') { - this.fetchKubernetesClusterServiceOfferingData() + this.fetchKubernetesClusterServiceOfferingData(this.resource.serviceofferingid, 'default') + this.fetchKubernetesClusterServiceOfferingData(this.resource.workerofferingid, 'worker') + this.fetchKubernetesClusterServiceOfferingData(this.resource.controlofferingid, 'control') + if (this.resource.etcdofferingid && this.resource.etcdnodes && this.resource.etcdnodes > 0) { + this.fetchKubernetesClusterServiceOfferingData(this.resource.controlofferingid, 'etcd') + } return } this.fetchKubernetesVersionData() @@ -167,19 +232,21 @@ export default { isObjectEmpty (obj) { return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object) }, - fetchKubernetesClusterServiceOfferingData () { + fetchKubernetesClusterServiceOfferingData (offeringId, type) { const params = {} if (!this.isObjectEmpty(this.resource)) { - params.id = this.resource.serviceofferingid + params.id = offeringId } + var minCpu = 0 + var minMemory = 0 api('listServiceOfferings', params).then(json => { var items = json?.listserviceofferingsresponse?.serviceoffering || [] if (this.arrayHasItems(items) && !this.isObjectEmpty(items[0])) { - this.minCpu = items[0].cpunumber - this.minMemory = items[0].memory + minCpu = items[0].cpunumber + minMemory = items[0].memory } }).finally(() => { - this.fetchServiceOfferingData() + this.fetchServiceOfferingData(minCpu, minMemory, type) }) }, fetchKubernetesVersionData () { @@ -187,21 +254,28 @@ export default { if (!this.isObjectEmpty(this.resource)) { params.id = this.resource.kubernetesversionid } + var minCpu = 0 + var minMemory = 0 api('listKubernetesSupportedVersions', params).then(json => { const versionObjs = json?.listkubernetessupportedversionsresponse?.kubernetessupportedversion || [] if (this.arrayHasItems(versionObjs) && !this.isObjectEmpty(versionObjs[0])) { - this.minCpu = versionObjs[0].mincpunumber - this.minMemory = versionObjs[0].minmemory + minCpu = versionObjs[0].mincpunumber + minMemory = versionObjs[0].minmemory } }).finally(() => { - this.fetchServiceOfferingData() + this.fetchServiceOfferingData(minCpu, minMemory, 'default') + this.fetchServiceOfferingData(minCpu, minMemory, 'worker') + this.fetchServiceOfferingData(minCpu, minMemory, 'control') + if (this.resource.etcdofferingid && this.resource.etcdnodes && this.resource.etcdnodes > 0) { + this.fetchServiceOfferingData(minCpu, minMemory, 'etcd') + } }) }, - fetchServiceOfferingData () { - this.serviceOfferings = [] + fetchServiceOfferingData (minCpu, minMemory, type) { + var offerings = [] const params = { - cpunumber: this.minCpu, - memory: this.minMemory + cpunumber: minCpu, + memory: minMemory } this.serviceOfferingLoading = true api('listServiceOfferings', params).then(json => { @@ -209,17 +283,35 @@ export default { if (this.arrayHasItems(items)) { for (var i = 0; i < items.length; i++) { if (items[i].iscustomized === false) { - this.serviceOfferings.push(items[i]) + offerings.push(items[i]) } } } }).finally(() => { this.serviceOfferingLoading = false - if (this.arrayHasItems(this.serviceOfferings)) { - for (var i = 0; i < this.serviceOfferings.length; i++) { - if (this.serviceOfferings[i].id === this.resource.serviceofferingid) { + if (this.arrayHasItems(offerings)) { + if (type === 'default') { + this.serviceOfferings = offerings + } else if (type === 'worker') { + this.workerOfferings = offerings + } else if (type === 'control') { + this.controlOfferings = offerings + } else if (type === 'etcd') { + this.etcdOfferings = offerings + } + for (var i = 0; i < offerings.length; i++) { + if (type === 'default' && offerings[i].id === this.resource.serviceofferingid) { this.form.serviceofferingid = i break + } else if (type === 'worker' && offerings[i].id === this.resource.workerofferingid) { + this.form.workerofferingid = i + break + } else if (type === 'control' && offerings[i].id === this.resource.controlofferingid) { + this.form.controlofferingid = i + break + } else if (type === 'etcd' && offerings[i].id === this.resource.etcdofferingid) { + this.form.etcdofferingid = i + break } } }