From c601ff364311f0aae04b40b1672ea47bb487c327 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 16 May 2018 17:50:09 +0100 Subject: Create user defined "NodePort" services per pod This feature will give the user the ability to create user defined "NodePort" services per pod, a part from the default SSH port created, by default, for each pod created. Example of Kubernetes yaml definition: apiVersion: v1 kind: Servicemeta data: name: pod-1-service-nodeport spec: type: NodePort ports: - name: web port: 80 targetPort: 8888 nodePort: 33333 clusterIP: 10.254.0.8 selector: app: pod-1 Example of Yardstick test case definition: context: type: Kubernetes servers: host: containers: - image: ... securityContext: allowPrivilegeEscalation: false - image: ... node_ports: - port: # Mandatory name: # Optional targetPort: # Optional, default: targetPort=port nodePort: # Optional, assigned by Kubernetes Kubernetes service, type "NodePort" [1] [1] https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport JIRA: YARDSTICK-1181 Change-Id: Ia7730e4569d5526d573402a98c27d55c5c82cdef Signed-off-by: Rodolfo Alonso Hernandez --- yardstick/orchestrator/kubernetes.py | 57 ++++++++++++++-------- .../unit/benchmark/contexts/test_kubernetes.py | 4 +- .../tests/unit/orchestrator/test_kubernetes.py | 40 +++++++++++++++ 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index 120c40b9f..a595de86d 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -182,33 +182,49 @@ class ReplicationControllerObject(object): annotations) -class ServiceObject(object): +class ServiceNodePortObject(object): - def __init__(self, name): - self.name = '{}-service'.format(name) + def __init__(self, name, **kwargs): + """Service kind "NodePort" object + + :param name: (string) name of the Service + :param kwargs: (dict) node_ports -> (list) port, name, targetPort, + nodePort + """ + self._name = '{}-service'.format(name) self.template = { - 'metadata': { - 'name': '{}-service'.format(name) - }, + 'metadata': {'name': '{}-service'.format(name)}, 'spec': { 'type': 'NodePort', - 'ports': [ - { - 'port': 22, - 'protocol': 'TCP' - } - ], - 'selector': { - 'app': name - } + 'ports': [], + 'selector': {'app': name} } } + self._add_port(22, protocol='TCP') + node_ports = copy.deepcopy(kwargs.get('node_ports', [])) + for port in node_ports: + port_number = port.pop('port') + self._add_port(port_number, **port) + + def _add_port(self, port, protocol=None, name=None, targetPort=None, + nodePort=None): + _port = {'port': port} + if protocol: + _port['protocol'] = protocol + if name: + _port['name'] = name + if targetPort: + _port['targetPort'] = targetPort + if nodePort: + _port['nodePort'] = nodePort + self.template['spec']['ports'].append(_port) + def create(self): k8s_utils.create_service(self.template) def delete(self): - k8s_utils.delete_service(self.name) + k8s_utils.delete_service(self._name) class CustomResourceDefinitionObject(object): @@ -359,11 +375,12 @@ class KubernetesTemplate(object): self.name = name self.ssh_key = '{}-key'.format(name) - self.rcs = [self._get_rc_name(rc) for rc in servers_cfg] + self.rcs = {self._get_rc_name(rc): cfg + for rc, cfg in servers_cfg.items()} self.k8s_objs = [ReplicationControllerObject( - self._get_rc_name(rc), ssh_key=self.ssh_key, **cfg) - for rc, cfg in servers_cfg.items()] - self.service_objs = [ServiceObject(s) for s in self.rcs] + rc, ssh_key=self.ssh_key, **cfg) for rc, cfg in self.rcs.items()] + self.service_objs = [ServiceNodePortObject(rc, **cfg) + for rc, cfg in self.rcs.items()] self.crd = [CustomResourceDefinitionObject(self.name, **crd) for crd in crd_cfg] self.network_objs = [NetworkObject(**nobj) for nobj in networks_cfg] diff --git a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py index 0d698c5bd..fd7b4f287 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py +++ b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py @@ -163,12 +163,12 @@ class KubernetesTestCase(unittest.TestCase): self.k8s_context._get_node_ip() mock_get_node_list.assert_called_once() - @mock.patch('yardstick.orchestrator.kubernetes.ServiceObject.create') + @mock.patch.object(orchestrator_kubernetes.ServiceNodePortObject, 'create') def test_create_services(self, mock_create): self.k8s_context._create_services() mock_create.assert_called() - @mock.patch('yardstick.orchestrator.kubernetes.ServiceObject.delete') + @mock.patch.object(orchestrator_kubernetes.ServiceNodePortObject, 'delete') def test_delete_services(self, mock_delete): self.k8s_context._delete_services() mock_delete.assert_called() diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py index e8d5abf67..70d17e216 100644 --- a/yardstick/tests/unit/orchestrator/test_kubernetes.py +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -399,3 +399,43 @@ class NetworkObjectTestCase(base.BaseUnitTestCase): net_obj.delete() mock_delete_network.assert_called_once_with( 'scope', 'group', 'version', 'plural', 'name') + + +class ServiceNodePortObjectTestCase(base.BaseUnitTestCase): + + def test__init(self): + with mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') \ + as mock_add_port: + kubernetes.ServiceNodePortObject('fake_name', + node_ports=[{'port': 80}]) + + mock_add_port.assert_has_calls([mock.call(22, protocol='TCP'), + mock.call(80)]) + + def test__add_port(self): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + port_ssh = {'port': 22, + 'protocol': 'TCP',} + port_definition = {'port': 80, + 'protocol': 'TCP', + 'name': 'web', + 'targetPort': 10080, + 'nodePort': 30080} + port = copy.deepcopy(port_definition) + port.pop('port') + nodeport_object._add_port(80, **port) + self.assertEqual([port_ssh, port_definition], + nodeport_object.template['spec']['ports']) + + @mock.patch.object(kubernetes_utils, 'create_service') + def test_create(self, mock_create_service): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + nodeport_object.template = 'fake_template' + nodeport_object.create() + mock_create_service.assert_called_once_with('fake_template') + + @mock.patch.object(kubernetes_utils, 'delete_service') + def test_delete(self, mock_delete_service): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + nodeport_object.delete() + mock_delete_service.assert_called_once_with('fake_name-service') -- cgit 1.2.3-korg