Skip to content

Commit 90eb42a

Browse files
committed
Add marvin test and fix update template for cks and since annotations
1 parent 258a2cb commit 90eb42a

File tree

6 files changed

+219
-18
lines changed

6 files changed

+219
-18
lines changed

api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use
4848

4949
@Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN,
5050
description = "indicates that the template can be used for deployment of CKS clusters",
51-
since = "4.20.0")
51+
since = "4.21.0")
5252
private Boolean forCks;
5353

5454
/////////////////////////////////////////////////////
@@ -68,8 +68,8 @@ public String getTemplateTag() {
6868
return templateTag;
6969
}
7070

71-
public boolean getForCks() {
72-
return Boolean.TRUE.equals(forCks);
71+
public Boolean getForCks() {
72+
return forCks;
7373
}
7474

7575
/////////////////////////////////////////////////////

plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/AddNodesToKubernetesClusterCmd.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
@APICommand(name = "addNodesToKubernetesCluster",
4040
description = "Add nodes as workers to an existing CKS cluster. ",
4141
responseObject = KubernetesClusterResponse.class,
42-
since = "4.20.0",
42+
since = "4.21.0",
4343
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
4444
public class AddNodesToKubernetesClusterCmd extends BaseAsyncCmd {
4545

@@ -53,22 +53,22 @@ public class AddNodesToKubernetesClusterCmd extends BaseAsyncCmd {
5353
description = "comma separated list of (external) node (physical or virtual machines) IDs that need to be" +
5454
"added as worker nodes to an existing managed Kubernetes cluster (CKS)",
5555
required = true,
56-
since = "4.20.0")
56+
since = "4.21.0")
5757
private List<Long> nodeIds;
5858

5959
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true,
6060
entityType = KubernetesClusterResponse.class,
61-
description = "the ID of the Kubernetes cluster", since = "4.20.0")
61+
description = "the ID of the Kubernetes cluster", since = "4.21.0")
6262
private Long clusterId;
6363

6464
@Parameter(name = ApiConstants.MOUNT_CKS_ISO_ON_VR, type = CommandType.BOOLEAN,
6565
description = "(optional) Vmware only, uses the CKS cluster network VR to mount the CKS ISO",
66-
since = "4.20.0")
66+
since = "4.21.0")
6767
private Boolean mountCksIsoOnVr;
6868

6969
@Parameter(name = ApiConstants.MANUAL_UPGRADE, type = CommandType.BOOLEAN,
7070
description = "(optional) indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation",
71-
since = "4.20.0")
71+
since = "4.21.0")
7272
private Boolean manualUpgrade;
7373

7474
/////////////////////////////////////////////////////

plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/RemoveNodesFromKubernetesClusterCmd.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
@APICommand(name = "removeNodesFromKubernetesCluster",
4444
description = "Removes external nodes from a CKS cluster. ",
4545
responseObject = KubernetesClusterResponse.class,
46-
since = "4.20.0",
46+
since = "4.21.0",
4747
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
4848
public class RemoveNodesFromKubernetesClusterCmd extends BaseAsyncCmd {
4949

@@ -59,12 +59,12 @@ public class RemoveNodesFromKubernetesClusterCmd extends BaseAsyncCmd {
5959
description = "comma separated list of node (physical or virtual machines) IDs that need to be" +
6060
"removed from the Kubernetes cluster (CKS)",
6161
required = true,
62-
since = "4.20.0")
62+
since = "4.21.0")
6363
private List<Long> nodeIds;
6464

6565
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true,
6666
entityType = KubernetesClusterResponse.class,
67-
description = "the ID of the Kubernetes cluster", since = "4.20.0")
67+
description = "the ID of the Kubernetes cluster", since = "4.21.0")
6868
private Long clusterId;
6969

7070
/////////////////////////////////////////////////////

server/src/main/java/com/cloud/template/TemplateManagerImpl.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,7 +2130,7 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) {
21302130
Map details = cmd.getDetails();
21312131
Account account = CallContext.current().getCallingAccount();
21322132
boolean cleanupDetails = cmd.isCleanupDetails();
2133-
boolean forCks = cmd instanceof UpdateTemplateCmd && ((UpdateTemplateCmd) cmd).getForCks();
2133+
Boolean forCks = cmd instanceof UpdateTemplateCmd ? ((UpdateTemplateCmd) cmd).getForCks() : null;
21342134
CPU.CPUArch arch = cmd.getCPUArch();
21352135

21362136
// verify that template exists
@@ -2180,6 +2180,7 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) {
21802180
isRoutingTemplate == null &&
21812181
templateType == null &&
21822182
templateTag == null &&
2183+
forCks == null &&
21832184
arch == null &&
21842185
(! cleanupDetails && details == null) //update details in every case except this one
21852186
);
@@ -2284,7 +2285,9 @@ else if (details != null && !details.isEmpty()) {
22842285
template.setDetails(details);
22852286
_tmpltDao.saveDetails(template);
22862287
}
2287-
template.setForCks(forCks);
2288+
if (forCks != null) {
2289+
template.setForCks(forCks);
2290+
}
22882291

22892292
_tmpltDao.update(id, template);
22902293

test/integration/smoke/test_kubernetes_clusters.py

Lines changed: 201 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
destroyVirtualMachine,
3636
deleteNetwork,
3737
addVirtualMachinesToKubernetesCluster,
38-
removeVirtualMachinesFromKubernetesCluster)
38+
removeVirtualMachinesFromKubernetesCluster,
39+
addNodesToKubernetesCluster,
40+
removeNodesFromKubernetesCluster)
3941
from marvin.cloudstackException import CloudstackAPIException
4042
from marvin.codes import PASS, FAILED
4143
from marvin.lib.base import (Template,
@@ -49,28 +51,84 @@
4951
VPC,
5052
NetworkACLList,
5153
NetworkACL,
52-
VirtualMachine)
54+
VirtualMachine,
55+
PublicIPAddress,
56+
FireWallRule,
57+
NATRule)
5358
from marvin.lib.utils import (cleanup_resources,
5459
validateList,
5560
random_gen)
5661
from marvin.lib.common import (get_zone,
5762
get_domain,
58-
get_template)
63+
get_template,
64+
get_test_template)
5965
from marvin.sshClient import SshClient
6066
from nose.plugins.attrib import attr
6167
from marvin.lib.decoratorGenerators import skipTestIf
6268

6369
from kubernetes import client, config
64-
import time, io, yaml
70+
import time, io, yaml, random
6571

6672
_multiprocess_shared_ = True
6773

6874
k8s_cluster = None
75+
k8s_cluster_node_offerings = None
6976
VPC_DATA = {
7077
"cidr": "10.1.0.0/22",
7178
"tier1_gateway": "10.1.1.1",
7279
"tier_netmask": "255.255.255.0"
7380
}
81+
RAND_SUFFIX = random_gen()
82+
NODES_TEMPLATE = {
83+
"kvm": {
84+
"name": "cks-u2204-kvm-" + RAND_SUFFIX,
85+
"displaytext": "cks-u2204-kvm-" + RAND_SUFFIX,
86+
"format": "qcow2",
87+
"hypervisor": "kvm",
88+
"ostypeid": "5d83ac5d-d03c-4743-9629-7d70b5928f7f",
89+
"url": "https://download.cloudstack.org/testing/custom_templates/ubuntu/22.04/cks-ubuntu-2204-kvm.qcow2.bz2",
90+
"requireshvm": "True",
91+
"ispublic": "True",
92+
"isextractable": "True",
93+
"forcks": "True"
94+
},
95+
"xenserver": {
96+
"name": "cks-u2204-hyperv-" + RAND_SUFFIX,
97+
"displaytext": "cks-u2204-hyperv-" + RAND_SUFFIX,
98+
"format": "vhd",
99+
"hypervisor": "xenserver",
100+
"ostypeid": "5d83ac5d-d03c-4743-9629-7d70b5928f7f",
101+
"url": "https://download.cloudstack.org/testing/custom_templates/ubuntu/22.04/cks-ubuntu-2204-hyperv.vhd.zip",
102+
"requireshvm": "True",
103+
"ispublic": "True",
104+
"isextractable": "True",
105+
"forcks": "True"
106+
},
107+
"hyperv": {
108+
"name": "cks-u2204-hyperv-" + RAND_SUFFIX,
109+
"displaytext": "cks-u2204-hyperv-" + RAND_SUFFIX,
110+
"format": "vhd",
111+
"hypervisor": "hyperv",
112+
"ostypeid": "5d83ac5d-d03c-4743-9629-7d70b5928f7f",
113+
"url": "https://download.cloudstack.org/testing/custom_templates/ubuntu/22.04/cks-ubuntu-2204-hyperv.vhd.zip",
114+
"requireshvm": "True",
115+
"ispublic": "True",
116+
"isextractable": "True",
117+
"forcks": "True"
118+
},
119+
"vmware": {
120+
"name": "cks-u2204-vmware-" + RAND_SUFFIX,
121+
"displaytext": "cks-u2204-vmware-" + RAND_SUFFIX,
122+
"format": "ova",
123+
"hypervisor": "vmware",
124+
"ostypeid": "5d83ac5d-d03c-4743-9629-7d70b5928f7f",
125+
"url": "https://download.cloudstack.org/testing/custom_templates/ubuntu/22.04/cks-ubuntu-2204-vmware.ova",
126+
"requireshvm": "True",
127+
"ispublic": "True",
128+
"isextractable": "True",
129+
"forcks": "True"
130+
}
131+
}
74132

75133
class TestKubernetesCluster(cloudstackTestCase):
76134

@@ -84,6 +142,7 @@ def setUpClass(cls):
84142
cls.mgtSvrDetails = cls.config.__dict__["mgtSvr"][0].__dict__
85143

86144
cls.hypervisorNotSupported = False
145+
cls.hypervisorIsNotVmware = cls.hypervisor.lower() != "vmware"
87146
if cls.hypervisor.lower() not in ["kvm", "vmware", "xenserver"]:
88147
cls.hypervisorNotSupported = True
89148
cls.setup_failed = False
@@ -129,6 +188,15 @@ def setUpClass(cls):
129188
(cls.services["cks_kubernetes_versions"][cls.k8s_version_to]["semanticversion"], cls.services["cks_kubernetes_versions"][cls.k8s_version_to]["url"], e))
130189

131190
if cls.setup_failed == False:
191+
cls.nodes_template = None
192+
cls.mgmtSshKey = None
193+
if cls.hypervisor.lower() == "vmware":
194+
cls.nodes_template = get_test_template(cls.apiclient,
195+
cls.zone.id,
196+
cls.hypervisor,
197+
NODES_TEMPLATE)
198+
cls.nodes_template.update(cls.apiclient, forcks=True)
199+
cls.mgmtSshKey = cls.getMgmtSshKey()
132200
cks_offering_data = cls.services["cks_service_offering"]
133201
cks_offering_data["name"] = 'CKS-Instance-' + random_gen()
134202
cls.cks_service_offering = ServiceOffering.create(
@@ -222,6 +290,19 @@ def updateVmwareSettings(cls, tearDown):
222290
name="vmware.create.full.clone",
223291
value=value)
224292

293+
@classmethod
294+
def getMgmtSshKey(cls):
295+
"""Get the management server SSH public key"""
296+
sshClient = SshClient(
297+
cls.mgtSvrDetails["mgtSvrIp"],
298+
22,
299+
cls.mgtSvrDetails["user"],
300+
cls.mgtSvrDetails["passwd"]
301+
)
302+
command = "cat /var/cloudstack/management/.ssh/id_rsa.pub"
303+
response = sshClient.execute(command)
304+
return str(response[0])
305+
225306
@classmethod
226307
def restartServer(cls):
227308
"""Restart management server"""
@@ -666,8 +747,12 @@ def test_11_test_unmanaged_cluster_lifecycle(self):
666747
def test_12_test_deploy_cluster_different_offerings_per_node_type(self):
667748
"""Test creating a CKS cluster with different offerings per node type
668749
669-
# Validate the following:
750+
# Validate the following on Kubernetes cluster creation:
751+
# - Use a service offering for control nodes
752+
# - Use a service offering for worker nodes
670753
"""
754+
if self.setup_failed == True:
755+
self.fail("Setup incomplete")
671756
cluster = self.getValidKubernetesCluster(worker_offering=self.cks_worker_nodes_offering,
672757
control_offering=self.cks_control_nodes_offering)
673758
self.assertEqual(
@@ -685,6 +770,117 @@ def test_12_test_deploy_cluster_different_offerings_per_node_type(self):
685770
0,
686771
"No Etcd Nodes expected but got {}".format(cluster.etcdnodes)
687772
)
773+
self.debug("Deleting Kubernetes cluster with ID: %s" % cluster.id)
774+
self.deleteKubernetesClusterAndVerify(cluster.id)
775+
return
776+
777+
@attr(tags=["advanced", "smoke"], required_hardware="true")
778+
@skipTestIf("hypervisorIsNotVmware")
779+
def test_13_test_add_external_nodes_to_cluster(self):
780+
"""Test creating a CKS cluster with different offerings per node type
781+
782+
# Validate the following:
783+
# - Deploy Kubernetes Cluster
784+
# - Deploy VM on the same network as the Kubernetes cluster with the worker nodes offering and CKS ready template
785+
# - Add external node to the Kubernetes Cluster
786+
"""
787+
if self.setup_failed == True:
788+
self.fail("Setup incomplete")
789+
cluster = self.getValidKubernetesCluster(worker_offering=self.cks_worker_nodes_offering,
790+
control_offering=self.cks_control_nodes_offering)
791+
self.assertEqual(
792+
cluster.size,
793+
1,
794+
"Expected 1 worker node but got {}".format(cluster.size)
795+
)
796+
self.services["virtual_machine"]["template"] = self.nodes_template.id
797+
external_node = VirtualMachine.create(self.apiclient,
798+
self.services["virtual_machine"],
799+
zoneid=self.zone.id,
800+
accountid=self.account.name,
801+
domainid=self.account.domainid,
802+
serviceofferingid=self.cks_worker_nodes_offering.id,
803+
networkids=cluster.networkid)
804+
805+
# Acquire public IP and create Port Forwarding Rule and Firewall rule for SSH access
806+
free_ip_addresses = PublicIPAddress.list(
807+
self.apiclient,
808+
domainid=self.account.domainid,
809+
account=self.account.name,
810+
forvirtualnetwork=True,
811+
state='Free'
812+
)
813+
random.shuffle(free_ip_addresses)
814+
external_node_ip = free_ip_addresses[0]
815+
external_node_ipaddress = PublicIPAddress.create(
816+
self.apiclient,
817+
zoneid=self.zone.id,
818+
networkid=cluster.networkid,
819+
ipaddress=external_node_ip.ipaddress
820+
)
821+
self.debug("Creating Firewall rule for VM ID: %s" % external_node.id)
822+
fw_rule = FireWallRule.create(
823+
self.apiclient,
824+
ipaddressid=external_node_ip.id,
825+
protocol='TCP',
826+
cidrlist=['0.0.0.0/0'],
827+
startport=22,
828+
endport=22
829+
)
830+
pf_rule = {
831+
"privateport": 22,
832+
"publicport": 22,
833+
"protocol": "TCP"
834+
}
835+
nat_rule = NATRule.create(
836+
self.apiclient,
837+
external_node,
838+
pf_rule,
839+
ipaddressid=external_node_ip.id
840+
)
841+
842+
# Add the management server SSH key to the authorized hosts on the external node
843+
node_ssh_client = SshClient(
844+
external_node_ip.ipaddress,
845+
22,
846+
'cloud',
847+
'cloud',
848+
retries=30,
849+
delay=10
850+
)
851+
node_ssh_client.execute("echo '" + self.mgmtSshKey + "' > ~/.ssh/authorized_keys")
852+
853+
self.addExternalNodesToKubernetesCluster(cluster.id, [external_node.id])
854+
self.assertEqual(
855+
cluster.size,
856+
2,
857+
"Expected 2 worker nodes but got {}".format(cluster.size)
858+
)
859+
self.removeExternalNodesFromKubernetesCluster(cluster.id, [external_node.id])
860+
self.assertEqual(
861+
cluster.size,
862+
1,
863+
"Expected 1 worker node but got {}".format(cluster.size)
864+
)
865+
nat_rule.delete(self.apiclient)
866+
fw_rule.delete(self.apiclient)
867+
external_node_ipaddress.delete(self.apiclient)
868+
VirtualMachine.delete(external_node, self.apiclient, expunge=True)
869+
self.debug("Deleting Kubernetes cluster with ID: %s" % cluster.id)
870+
self.deleteKubernetesClusterAndVerify(cluster.id)
871+
return
872+
873+
def addExternalNodesToKubernetesCluster(self, cluster_id, vm_list):
874+
cmd = addNodesToKubernetesCluster.addNodesToKubernetesClusterCmd()
875+
cmd.id = cluster_id
876+
cmd.nodeids = vm_list
877+
return self.apiclient.addNodesToKubernetesCluster(cmd)
878+
879+
def removeExternalNodesFromKubernetesCluster(self, cluster_id, vm_list):
880+
cmd = removeNodesFromKubernetesCluster.removeNodesFromKubernetesClusterCmd()
881+
cmd.id = cluster_id
882+
cmd.nodeids = vm_list
883+
return self.apiclient.removeNodesFromKubernetesCluster(cmd)
688884

689885
def addVirtualMachinesToKubernetesCluster(self, cluster_id, vm_list):
690886
cmd = addVirtualMachinesToKubernetesCluster.addVirtualMachinesToKubernetesClusterCmd()

tools/apidoc/gen_toc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@
256256
'deleteASNRange': 'AS Number Range',
257257
'listASNumbers': 'AS Number',
258258
'releaseASNumber': 'AS Number',
259+
'addNodesToKubernetesCluster': 'Kubernetes Service',
260+
'removeNodesFromKubernetesCluster': 'Kubernetes Service'
259261
}
260262

261263

0 commit comments

Comments
 (0)