Add "ports" parameters in Kubernetes context
[yardstick.git] / yardstick / tests / unit / orchestrator / test_kubernetes.py
1 ##############################################################################
2 # Copyright (c) 2017 Intel Corporation
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import copy
11
12 import mock
13
14 from yardstick.common import exceptions
15 from yardstick.common import kubernetes_utils
16 from yardstick.orchestrator import kubernetes
17 from yardstick.tests.unit import base
18
19
20 class GetTemplateTestCase(base.BaseUnitTestCase):
21
22     def test_get_template(self):
23         output_t = {
24             "apiVersion": "v1",
25             "kind": "ReplicationController",
26             "metadata": {
27                 "name": "host-k8s-86096c30"
28             },
29             "spec": {
30                 "replicas": 1,
31                 "template": {
32                     "metadata": {
33                         "labels": {
34                             "app": "host-k8s-86096c30"
35                         }
36                     },
37                     "spec": {
38                         "containers": [
39                             {
40                                 "args": [
41                                     "-c",
42                                     "chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
43 service ssh restart;while true ; do sleep 10000; done"
44                                 ],
45                                 "command": [
46                                     "/bin/bash"
47                                 ],
48                                 "image": "openretriever/yardstick",
49                                 "name": "host-k8s-86096c30-container",
50                                 "volumeMounts": [
51                                     {
52                                         "mountPath": "/tmp/.ssh/",
53                                         "name": "k8s-86096c30-key",
54                                         "readOnly": False
55                                     }
56                                 ]
57                             }
58                         ],
59                         "volumes": [
60                             {
61                                 "configMap": {
62                                     "name": "k8s-86096c30-key"
63                                 },
64                                 "name": "k8s-86096c30-key"
65                             }
66                         ],
67                         "nodeSelector": {
68                             "kubernetes.io/hostname": "node-01"
69                         },
70                         "restartPolicy": "Always"
71                     }
72                 }
73             }
74         }
75         input_s = {
76             'command': '/bin/bash',
77             'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
78 service ssh restart;while true ; do sleep 10000; done'],
79             'ssh_key': 'k8s-86096c30-key',
80             'nodeSelector': {'kubernetes.io/hostname': 'node-01'},
81             'volumes': [],
82             'restartPolicy': 'Always'
83         }
84         name = 'host-k8s-86096c30'
85         output_r = kubernetes.ReplicationControllerObject(
86             name, **input_s).get_template()
87         self.assertEqual(output_r, output_t)
88
89     def test_get_template_invalid_restart_policy(self):
90         input_s = {'restartPolicy': 'invalid_option'}
91         name = 'host-k8s-86096c30'
92         with self.assertRaises(exceptions.KubernetesWrongRestartPolicy):
93             kubernetes.ReplicationControllerObject(
94                 name, **input_s).get_template()
95
96
97 class GetRcPodsTestCase(base.BaseUnitTestCase):
98
99     @mock.patch('yardstick.orchestrator.kubernetes.k8s_utils.get_pod_list')
100     def test_get_rc_pods(self, mock_get_pod_list):
101         servers = {
102             'host': {
103                 'image': 'openretriever/yardstick',
104                 'command': '/bin/bash',
105                 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
106 service ssh restart;while true ; do sleep 10000; done']
107             },
108             'target': {
109                 'image': 'openretriever/yardstick',
110                 'command': '/bin/bash',
111                 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
112 service ssh restart;while true ; do sleep 10000; done']
113             }
114         }
115         k8s_template = kubernetes.KubernetesTemplate('k8s-86096c30', servers)
116         mock_get_pod_list.return_value.items = []
117         pods = k8s_template.get_rc_pods()
118         self.assertEqual(pods, [])
119
120
121 class ReplicationControllerObjectTestCase(base.BaseUnitTestCase):
122
123     def test__init_one_container(self):
124         pod_name = 'pod_name'
125         _kwargs = {'args': ['arg1', 'arg2'],
126                    'image': 'fake_image',
127                    'command': 'fake_command'}
128         k8s_obj = kubernetes.ReplicationControllerObject(pod_name, **_kwargs)
129         self.assertEqual(1, len(k8s_obj._containers))
130         container = k8s_obj._containers[0]
131         self.assertEqual(['arg1', 'arg2'], container._args)
132         self.assertEqual('fake_image', container._image)
133         self.assertEqual(['fake_command'], container._command)
134         self.assertEqual([], container._volume_mounts)
135
136     def test__init_multipe_containers(self):
137         pod_name = 'pod_name'
138         containers = []
139         for i in range(5):
140             containers.append({'args': ['arg1', 'arg2'],
141                                'image': 'fake_image_%s' % i,
142                                'command': 'fake_command_%s' % i})
143         _kwargs = {'containers': containers}
144         k8s_obj = kubernetes.ReplicationControllerObject(pod_name, **_kwargs)
145         self.assertEqual(5, len(k8s_obj._containers))
146         for i in range(5):
147             container = k8s_obj._containers[i]
148             self.assertEqual(['arg1', 'arg2'], container._args)
149             self.assertEqual('fake_image_%s' % i, container._image)
150             self.assertEqual(['fake_command_%s' % i], container._command)
151             self.assertEqual([], container._volume_mounts)
152
153     def test__add_volumes(self):
154         volume1 = {'name': 'fake_sshkey',
155                    'configMap': {'name': 'fake_sshkey'}}
156         volume2 = {'name': 'volume2',
157                    'configMap': 'data'}
158         k8s_obj = kubernetes.ReplicationControllerObject(
159             'name', ssh_key='fake_sshkey', volumes=[volume2])
160         k8s_obj._add_volumes()
161         volumes = k8s_obj.template['spec']['template']['spec']['volumes']
162         self.assertEqual(sorted([volume1, volume2], key=lambda k: k['name']),
163                          sorted(volumes, key=lambda k: k['name']))
164
165     def test__add_volumes_no_volumes(self):
166         volume1 = {'name': 'fake_sshkey',
167                    'configMap': {'name': 'fake_sshkey'}}
168         k8s_obj = kubernetes.ReplicationControllerObject(
169             'name', ssh_key='fake_sshkey')
170         k8s_obj._add_volumes()
171         volumes = k8s_obj.template['spec']['template']['spec']['volumes']
172         self.assertEqual([volume1], volumes)
173
174     def test__create_ssh_key_volume(self):
175         expected = {'name': 'fake_sshkey',
176                     'configMap': {'name': 'fake_sshkey'}}
177         k8s_obj = kubernetes.ReplicationControllerObject(
178             'name', ssh_key='fake_sshkey')
179         self.assertEqual(expected, k8s_obj._create_ssh_key_volume())
180
181     def test__create_volume_item(self):
182         for vol_type in kubernetes_utils.get_volume_types():
183             volume = {'name': 'vol_name',
184                       vol_type: 'data'}
185             self.assertEqual(
186                 volume,
187                 kubernetes.ReplicationControllerObject.
188                     _create_volume_item(volume))
189
190     def test__create_volume_item_invalid_type(self):
191         volume = {'name': 'vol_name',
192                   'invalid_type': 'data'}
193         with self.assertRaises(exceptions.KubernetesTemplateInvalidVolumeType):
194             kubernetes.ReplicationControllerObject._create_volume_item(volume)
195
196     def test__add_security_context(self):
197         k8s_obj = kubernetes.ReplicationControllerObject('pod_name')
198         self.assertNotIn('securityContext',
199                          k8s_obj.template['spec']['template']['spec'])
200
201         k8s_obj._security_context = {'key_pod': 'value_pod'}
202         k8s_obj._add_security_context()
203         self.assertEqual(
204             {'key_pod': 'value_pod'},
205             k8s_obj.template['spec']['template']['spec']['securityContext'])
206
207     def test__add_security_context_by_init(self):
208         containers = []
209         for i in range(5):
210             containers.append(
211                 {'securityContext': {'key%s' % i: 'value%s' % i}})
212         _kwargs = {'containers': containers,
213                    'securityContext': {'key_pod': 'value_pod'}}
214         k8s_obj = kubernetes.ReplicationControllerObject('pod_name', **_kwargs)
215         self.assertEqual(
216             {'key_pod': 'value_pod'},
217             k8s_obj.template['spec']['template']['spec']['securityContext'])
218         for i in range(5):
219             container = (
220                 k8s_obj.template['spec']['template']['spec']['containers'][i])
221             self.assertEqual({'key%s' % i: 'value%s' % i},
222                              container['securityContext'])
223
224     def test__add_networks(self):
225         k8s_obj = kubernetes.ReplicationControllerObject(
226             'name', networks=['network1', 'network2', 'network3'])
227         k8s_obj._add_networks()
228         networks = k8s_obj.\
229             template['spec']['template']['metadata']['annotations']['networks']
230         expected = ('[{"name": "network1"}, {"name": "network2"}, '
231                     '{"name": "network3"}]')
232         self.assertEqual(expected, networks)
233
234
235 class ContainerObjectTestCase(base.BaseUnitTestCase):
236
237     def test__create_volume_mounts(self):
238         volume_mount = {'name': 'fake_name',
239                         'mountPath': 'fake_path'}
240         ssh_vol = {'name': 'fake_ssh_key',
241                    'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH,
242                    'readOnly': False}
243         expected = copy.deepcopy(volume_mount)
244         expected['readOnly'] = False
245         expected = [expected, ssh_vol]
246         container_obj = kubernetes.ContainerObject(
247             'cname', 'fake_ssh_key', volumeMounts=[volume_mount])
248         output = container_obj._create_volume_mounts()
249         self.assertEqual(expected, output)
250
251     def test__create_volume_mounts_no_volume_mounts(self):
252         ssh_vol = {'name': 'fake_ssh_key2',
253                    'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH,
254                    'readOnly': False}
255         container_obj = kubernetes.ContainerObject('name', 'fake_ssh_key2')
256         output = container_obj._create_volume_mounts()
257         self.assertEqual([ssh_vol], output)
258
259     def test__create_volume_mounts_item(self):
260         volume_mount = {'name': 'fake_name',
261                         'mountPath': 'fake_path'}
262         expected = copy.deepcopy(volume_mount)
263         expected['readOnly'] = False
264         output = kubernetes.ContainerObject._create_volume_mounts_item(
265             volume_mount)
266         self.assertEqual(expected, output)
267
268     def test_get_container_item(self):
269         volume_mount = {'name': 'fake_name',
270                         'mountPath': 'fake_path'}
271         args = ['arg1', 'arg2']
272         container_obj = kubernetes.ContainerObject(
273             'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
274             args=args)
275         expected = {'args': args,
276                     'command': [kubernetes.ContainerObject.COMMAND_DEFAULT],
277                     'image': kubernetes.ContainerObject.IMAGE_DEFAULT,
278                     'name': 'cname-container',
279                     'volumeMounts': container_obj._create_volume_mounts()}
280         self.assertEqual(expected, container_obj.get_container_item())
281
282     def test_get_container_item_with_security_context(self):
283         volume_mount = {'name': 'fake_name',
284                         'mountPath': 'fake_path'}
285         args = ['arg1', 'arg2']
286         container_obj = kubernetes.ContainerObject(
287             'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
288             args=args, securityContext={'key': 'value'})
289         expected = {'args': args,
290                     'command': [kubernetes.ContainerObject.COMMAND_DEFAULT],
291                     'image': kubernetes.ContainerObject.IMAGE_DEFAULT,
292                     'name': 'cname-container',
293                     'volumeMounts': container_obj._create_volume_mounts(),
294                     'securityContext': {'key': 'value'}}
295         self.assertEqual(expected, container_obj.get_container_item())
296
297     def test_get_container_item_with_env(self):
298         volume_mount = {'name': 'fake_name',
299                         'mountPath': 'fake_path'}
300         args = ['arg1', 'arg2']
301         container_obj = kubernetes.ContainerObject(
302             'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
303             args=args, env=[{'name': 'fake_var_name',
304                              'value': 'fake_var_value'}])
305         expected = {'args': args,
306                     'command': [kubernetes.ContainerObject.COMMAND_DEFAULT],
307                     'image': kubernetes.ContainerObject.IMAGE_DEFAULT,
308                     'name': 'cname-container',
309                     'volumeMounts': container_obj._create_volume_mounts(),
310                     'env': [{'name': 'fake_var_name',
311                              'value': 'fake_var_value'}]}
312         self.assertEqual(expected, container_obj.get_container_item())
313
314     def test_get_container_item_with_ports_multi_parameter(self):
315         volume_mount = {'name': 'fake_name',
316                         'mountPath': 'fake_path'}
317         args = ['arg1', 'arg2']
318         container_obj = kubernetes.ContainerObject(
319             'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
320             args=args, ports=[{'containerPort': 'fake_port_name',
321                                'hostPort': 'fake_host_port',
322                                'name': 'fake_name',
323                                'protocol': 'fake_protocol',
324                                'invalid_varible': 'fakeinvalid_varible',
325                                'hostIP': 'fake_port_number'}])
326         expected = {'args': args,
327                     'command': [
328                         kubernetes.ContainerObject.COMMAND_DEFAULT],
329                     'image': kubernetes.ContainerObject.IMAGE_DEFAULT,
330                     'name': 'cname-container',
331                     'volumeMounts': container_obj._create_volume_mounts(),
332                     'ports': [{'containerPort': 'fake_port_name',
333                                'hostPort': 'fake_host_port',
334                                'name': 'fake_name',
335                                'protocol': 'fake_protocol',
336                                'hostIP': 'fake_port_number'}]}
337         self.assertEqual(expected, container_obj.get_container_item())
338
339     def test_get_container_item_with_ports_no_container_port(self):
340         with self.assertRaises(exceptions.KubernetesContainerPortNotDefined):
341             volume_mount = {'name': 'fake_name',
342                             'mountPath': 'fake_path'}
343             args = ['arg1', 'arg2']
344             container_obj = kubernetes.ContainerObject(
345                     'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
346                     args=args, ports=[{'hostPort': 'fake_host_port',
347                                        'name': 'fake_name',
348                                        'protocol': 'fake_protocol',
349                                        'hostIP': 'fake_port_number'}])
350             container_obj.get_container_item()
351
352     def test_get_container_item_with_resources(self):
353         volume_mount = {'name': 'fake_name',
354                         'mountPath': 'fake_path'}
355         args = ['arg1', 'arg2']
356         resources = {'requests': {'key1': 'val1'},
357                      'limits': {'key2': 'val2'},
358                      'other_key': {'key3': 'val3'}}
359         container_obj = kubernetes.ContainerObject(
360             'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount],
361             args=args, resources=resources)
362         expected = {'args': args,
363                     'command': [kubernetes.ContainerObject.COMMAND_DEFAULT],
364                     'image': kubernetes.ContainerObject.IMAGE_DEFAULT,
365                     'name': 'cname-container',
366                     'volumeMounts': container_obj._create_volume_mounts(),
367                     'resources': {'requests': {'key1': 'val1'},
368                                   'limits': {'key2': 'val2'}}}
369         self.assertEqual(expected, container_obj.get_container_item())
370
371
372 class CustomResourceDefinitionObjectTestCase(base.BaseUnitTestCase):
373
374     def test__init(self):
375         template = {
376             'metadata': {
377                 'name': 'newcrds.ctx_name.com'
378             },
379             'spec': {
380                 'group': 'ctx_name.com',
381                 'version': 'v2',
382                 'scope': 'scope',
383                 'names': {'plural': 'newcrds',
384                           'singular': 'newcrd',
385                           'kind': 'Newcrd'}
386             }
387         }
388         crd_obj = kubernetes.CustomResourceDefinitionObject(
389             'ctx_name', name='newcrd', version='v2', scope='scope')
390         self.assertEqual('newcrds.ctx_name.com', crd_obj._name)
391         self.assertEqual(template, crd_obj._template)
392
393     def test__init_missing_parameter(self):
394         with self.assertRaises(exceptions.KubernetesCRDObjectDefinitionError):
395             kubernetes.CustomResourceDefinitionObject('ctx_name',
396                                                       noname='name')
397
398
399 class NetworkObjectTestCase(base.BaseUnitTestCase):
400
401     def setUp(self):
402         self.net_obj = kubernetes.NetworkObject(name='fake_name',
403                                                 plugin='fake_plugin',
404                                                 args='fake_args')
405
406     def test__init_missing_parameter(self):
407         with self.assertRaises(
408                 exceptions.KubernetesNetworkObjectDefinitionError):
409             kubernetes.NetworkObject(name='name', plugin='plugin')
410         with self.assertRaises(
411                 exceptions.KubernetesNetworkObjectDefinitionError):
412             kubernetes.NetworkObject(name='name', args='args')
413         with self.assertRaises(
414                 exceptions.KubernetesNetworkObjectDefinitionError):
415             kubernetes.NetworkObject(args='args', plugin='plugin')
416
417     @mock.patch.object(kubernetes_utils, 'get_custom_resource_definition')
418     def test_crd(self, mock_get_crd):
419         mock_crd = mock.Mock()
420         mock_get_crd.return_value = mock_crd
421         net_obj = copy.deepcopy(self.net_obj)
422         self.assertEqual(mock_crd, net_obj.crd)
423
424     def test_template(self):
425         net_obj = copy.deepcopy(self.net_obj)
426         expected = {'apiVersion': 'group.com/v2',
427                     'kind': kubernetes.NetworkObject.KIND,
428                     'metadata': {
429                         'name': 'fake_name'},
430                     'plugin': 'fake_plugin',
431                     'args': 'fake_args'}
432         crd = mock.Mock()
433         crd.spec.group = 'group.com'
434         crd.spec.version = 'v2'
435         net_obj._crd = crd
436         self.assertEqual(expected, net_obj.template)
437
438     def test_group(self):
439         net_obj = copy.deepcopy(self.net_obj)
440         net_obj._crd = mock.Mock()
441         net_obj._crd.spec.group = 'fake_group'
442         self.assertEqual('fake_group', net_obj.group)
443
444     def test_version(self):
445         net_obj = copy.deepcopy(self.net_obj)
446         net_obj._crd = mock.Mock()
447         net_obj._crd.spec.version = 'version_4'
448         self.assertEqual('version_4', net_obj.version)
449
450     def test_plural(self):
451         net_obj = copy.deepcopy(self.net_obj)
452         net_obj._crd = mock.Mock()
453         net_obj._crd.spec.names.plural = 'name_ending_in_s'
454         self.assertEqual('name_ending_in_s', net_obj.plural)
455
456     def test_scope(self):
457         net_obj = copy.deepcopy(self.net_obj)
458         net_obj._crd = mock.Mock()
459         net_obj._crd.spec.scope = 'Cluster'
460         self.assertEqual('Cluster', net_obj.scope)
461
462     @mock.patch.object(kubernetes_utils, 'create_network')
463     def test_create(self, mock_create_network):
464         net_obj = copy.deepcopy(self.net_obj)
465         net_obj._scope = 'scope'
466         net_obj._group = 'group'
467         net_obj._version = 'version'
468         net_obj._plural = 'plural'
469         net_obj._template = 'template'
470         net_obj.create()
471         mock_create_network.assert_called_once_with(
472             'scope', 'group', 'version', 'plural', 'template')
473
474     @mock.patch.object(kubernetes_utils, 'delete_network')
475     def test_delete(self, mock_delete_network):
476         net_obj = copy.deepcopy(self.net_obj)
477         net_obj._scope = 'scope'
478         net_obj._group = 'group'
479         net_obj._version = 'version'
480         net_obj._plural = 'plural'
481         net_obj._name = 'name'
482         net_obj.delete()
483         mock_delete_network.assert_called_once_with(
484             'scope', 'group', 'version', 'plural', 'name')
485
486
487 class ServiceNodePortObjectTestCase(base.BaseUnitTestCase):
488
489     def test__init(self):
490         with mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') \
491                 as mock_add_port:
492             kubernetes.ServiceNodePortObject('fake_name',
493                                              node_ports=[{'port': 80}])
494
495         mock_add_port.assert_has_calls([mock.call(22, protocol='TCP'),
496                                         mock.call(80)])
497
498     def test__add_port(self):
499         nodeport_object = kubernetes.ServiceNodePortObject('fake_name')
500         port_ssh = {'port': 22,
501                     'protocol': 'TCP',}
502         port_definition = {'port': 80,
503                            'protocol': 'TCP',
504                            'name': 'web',
505                            'targetPort': 10080,
506                            'nodePort': 30080}
507         port = copy.deepcopy(port_definition)
508         port.pop('port')
509         nodeport_object._add_port(80, **port)
510         self.assertEqual([port_ssh, port_definition],
511                          nodeport_object.template['spec']['ports'])
512
513     @mock.patch.object(kubernetes_utils, 'create_service')
514     def test_create(self, mock_create_service):
515         nodeport_object = kubernetes.ServiceNodePortObject('fake_name')
516         nodeport_object.template = 'fake_template'
517         nodeport_object.create()
518         mock_create_service.assert_called_once_with('fake_template')
519
520     @mock.patch.object(kubernetes_utils, 'delete_service')
521     def test_delete(self, mock_delete_service):
522         nodeport_object = kubernetes.ServiceNodePortObject('fake_name')
523         nodeport_object.delete()
524         mock_delete_service.assert_called_once_with('fake_name-service')