Add node type to service offering mapping parameter on createKubernetesCluster API

This commit is contained in:
nvazquez 2024-01-30 23:40:37 -03:00
parent 35357dc8f9
commit 2397c22f4a
No known key found for this signature in database
GPG Key ID: 656E1BCC8CB54F84
7 changed files with 192 additions and 0 deletions

View File

@ -21,6 +21,11 @@ import org.apache.cloudstack.acl.ControlledEntity;
public interface KubernetesClusterHelper extends Adapter {
enum KubernetesClusterNodeType {
WORKER, MASTER, ETCD
}
ControlledEntity findByUuid(String uuid);
ControlledEntity findByVmId(long vmId);
boolean isValidNodeType(String nodeType);
}

View File

@ -87,6 +87,8 @@ public interface VmDetailConstants {
String DEPLOY_AS_IS_CONFIGURATION = "configurationId";
String KEY_PAIR_NAMES = "keypairnames";
String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser";
String CKS_NODE_TYPE = "node";
String OFFERING = "offering";
// VMware to KVM VM migrations specific
String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm";

View File

@ -1036,6 +1036,7 @@ public class ApiConstants {
public static final String AUTOSCALING_ENABLED = "autoscalingenabled";
public static final String MIN_SIZE = "minsize";
public static final String MAX_SIZE = "maxsize";
public static final String NODE_TYPE_OFFERING_MAP = "nodetypeofferingmap";
public static final String BOOT_TYPE = "boottype";
public static final String BOOT_MODE = "bootmode";

View File

@ -22,6 +22,7 @@ import com.cloud.utils.component.AdapterBase;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
@ -49,6 +50,19 @@ public class KubernetesClusterHelperImpl extends AdapterBase implements Kubernet
return kubernetesClusterDao.findById(clusterVmMapVO.getClusterId());
}
@Override
public boolean isValidNodeType(String nodeType) {
if (StringUtils.isBlank(nodeType)) {
return false;
}
try {
KubernetesClusterNodeType.valueOf(nodeType.toUpperCase());
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
@Override
public String getConfigComponentName() {
return KubernetesClusterHelper.class.getSimpleName();

View File

@ -17,10 +17,15 @@
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.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;
@ -40,6 +45,7 @@ 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;
@ -62,6 +68,8 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
@Inject
public KubernetesClusterService kubernetesClusterService;
@Inject
protected KubernetesClusterHelper kubernetesClusterHelper;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
@ -87,6 +95,11 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
description = "the ID of the service offering for the virtual machines in the cluster.")
private Long serviceOfferingId;
@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;
@ACL(accessType = AccessType.UseEntry)
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the" +
" virtual machine. Must be used with domainId.")
@ -244,6 +257,28 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
return clusterType;
}
public Map<String, Long> getNodeTypeOfferingMap() {
Map<String, Long> mapping = new HashMap<>();
if (MapUtils.isNotEmpty(nodeTypeOfferingMap)) {
for (Map<String, String> entry : nodeTypeOfferingMap.values()) {
String nodeTypeStr = entry.get(VmDetailConstants.CKS_NODE_TYPE);
String serviceOfferingUuid = entry.get(VmDetailConstants.OFFERING);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Node Type: '%s' should use Service Offering ID: '%s'", nodeTypeStr, serviceOfferingUuid));
}
ServiceOffering serviceOffering = _entityMgr.findByUuid(ServiceOffering.class, serviceOfferingUuid);
if (StringUtils.isAnyEmpty(nodeTypeStr, serviceOfferingUuid) ||
!kubernetesClusterHelper.isValidNodeType(nodeTypeStr) ||
serviceOffering == null) {
throw new InvalidParameterValueException(String.format("Service Offering ID: %s for Node Type: %s is invalid", serviceOfferingUuid, nodeTypeStr));
}
KubernetesClusterHelper.KubernetesClusterNodeType nodeType = KubernetesClusterHelper.KubernetesClusterNodeType.valueOf(nodeTypeStr.toUpperCase());
mapping.put(nodeType.name(), serviceOffering.getId());
}
}
return mapping;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -0,0 +1,45 @@
// 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 com.cloud.kubernetes.cluster;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesClusterHelperImplTest {
private final KubernetesClusterHelperImpl helper = new KubernetesClusterHelperImpl();
@Test
public void testIsValidNodeTypeEmptyNodeType() {
Assert.assertFalse(helper.isValidNodeType(null));
}
@Test
public void testIsValidNodeTypeInvalidNodeType() {
String nodeType = "invalidNodeType";
Assert.assertFalse(helper.isValidNodeType(nodeType));
}
@Test
public void testIsValidNodeTypeValidNodeTypeLowercase() {
String nodeType = KubernetesClusterHelper.KubernetesClusterNodeType.WORKER.name().toLowerCase();
Assert.assertTrue(helper.isValidNodeType(nodeType));
}
}

View File

@ -0,0 +1,90 @@
// 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.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.MASTER;
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 masterServiceOffering;
private final CreateKubernetesClusterCmd cmd = new CreateKubernetesClusterCmd();
private static final String workerNodesOfferingId = UUID.randomUUID().toString();
private static final String masterNodesOfferingId = UUID.randomUUID().toString();
private static final Long workerOfferingId = 1L;
private static final Long masterOfferingId = 2L;
@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, masterNodesOfferingId)).thenReturn(masterServiceOffering);
Mockito.when(workerServiceOffering.getId()).thenReturn(workerOfferingId);
Mockito.when(masterServiceOffering.getId()).thenReturn(masterOfferingId);
}
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.nodeTypeOfferingMap = new HashMap<>();
Map<String, String> firstMap = createMapEntry(WORKER, workerNodesOfferingId);
Map<String, String> secondMap = createMapEntry(MASTER, masterNodesOfferingId);
cmd.nodeTypeOfferingMap.put("map1", firstMap);
cmd.nodeTypeOfferingMap.put("map2", secondMap);
Map<String, Long> map = cmd.getNodeTypeOfferingMap();
Assert.assertNotNull(map);
Assert.assertEquals(2, map.size());
Assert.assertTrue(map.containsKey(WORKER.name()) && map.containsKey(MASTER.name()));
Assert.assertEquals(workerOfferingId, map.get(WORKER.name()));
Assert.assertEquals(masterOfferingId, map.get(MASTER.name()));
}
}