From 34292694f596561de9c78f0feb663ffa5de0dc2d Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Sat, 5 May 2018 11:13:28 +0100 Subject: Define several containers per pod in a replication controller Add the ability to define not only one but many containers per pod in a replication controller descriptor. This feature must be backwards compatible; all current test cases using the "single container" server definition must be accepted. Example of single container pod definition: context: type: Kubernetes servers: host: image: ... commands: ... volumes: - name: volume1 # mandatory # mandatory Example of several container pod definition: context: type: Kubernetes servers: host: containers: # if this key is present, all container specific parameters (image, commands, args, volumeMounts, etc.) must be defined per container - image: ... commands: ... - image: ... commands: ... volumes: - name: volume1 # mandatory # mandatory NOTE: other parameters, like "volumes" or "nodeSelector", are common to all containers in the pod. JIRA: YARDSTICK-1155 Change-Id: Ib95668c68e9c09e6de3f1aa41c903cc52e6809ad Signed-off-by: Rodolfo Alonso Hernandez --- yardstick/orchestrator/kubernetes.py | 87 +++++++++++++--------- .../tests/unit/orchestrator/test_kubernetes.py | 65 ++++++++++++---- 2 files changed, 102 insertions(+), 50 deletions(-) diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index e05c971ac..8ccb98853 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -14,23 +14,66 @@ from yardstick.common import utils from yardstick.common import kubernetes_utils as k8s_utils -class KubernetesObject(object): +class ContainerObject(object): SSH_MOUNT_PATH = '/tmp/.ssh/' IMAGE_DEFAULT = 'openretriever/yardstick' COMMAND_DEFAULT = '/bin/bash' + + def __init__(self, name, ssh_key, **kwargs): + self._name = name + self._ssh_key = ssh_key + self._image = kwargs.get('image', self.IMAGE_DEFAULT) + self._command = [kwargs.get('command', self.COMMAND_DEFAULT)] + self._args = kwargs.get('args', []) + self._volume_mounts = kwargs.get('volumeMounts', []) + + def _create_volume_mounts(self): + """Return all "volumeMounts" items per container""" + volume_mounts_items = [self._create_volume_mounts_item(vol) + for vol in self._volume_mounts] + ssh_vol = {'name': self._ssh_key, + 'mountPath': self.SSH_MOUNT_PATH} + volume_mounts_items.append(self._create_volume_mounts_item(ssh_vol)) + return volume_mounts_items + + @staticmethod + def _create_volume_mounts_item(volume_mount): + """Create a "volumeMounts" item""" + return {'name': volume_mount['name'], + 'mountPath': volume_mount['mountPath'], + 'readOnly': volume_mount.get('readOnly', False)} + + def get_container_item(self): + """Create a "container" item""" + container_name = '{}-container'.format(self._name) + return {'args': self._args, + 'command': self._command, + 'image': self._image, + 'name': container_name, + 'volumeMounts': self._create_volume_mounts()} + + +class KubernetesObject(object): + SSHKEY_DEFAULT = 'yardstick_key' def __init__(self, name, **kwargs): super(KubernetesObject, self).__init__() + parameters = copy.deepcopy(kwargs) self.name = name - self.image = kwargs.get('image', self.IMAGE_DEFAULT) - self.command = [kwargs.get('command', self.COMMAND_DEFAULT)] - self.args = kwargs.get('args', []) - self.ssh_key = kwargs.get('ssh_key', self.SSHKEY_DEFAULT) - self.node_selector = kwargs.get('nodeSelector', {}) - self._volumes = kwargs.get('volumes', []) - self._volume_mounts = kwargs.get('volumeMounts', []) + self.node_selector = parameters.pop('nodeSelector', {}) + self.ssh_key = parameters.pop('ssh_key', self.SSHKEY_DEFAULT) + self._volumes = parameters.pop('volumes', []) + + containers = parameters.pop('containers', None) + if containers: + self._containers = [ + ContainerObject(self.name, self.ssh_key, **container) + for container in containers] + else: + self._containers = [ + ContainerObject(self.name, self.ssh_key, **parameters)] self.template = { "apiVersion": "v1", @@ -71,20 +114,12 @@ class KubernetesObject(object): name) def _add_containers(self): - containers = [self._create_container_item()] + containers = [container.get_container_item() + for container in self._containers] utils.set_dict_value(self.template, 'spec.template.spec.containers', containers) - def _create_container_item(self): - """Create a "container" item""" - container_name = '{}-container'.format(self.name) - return {'args': self.args, - 'command': self.command, - 'image': self.image, - 'name': container_name, - 'volumeMounts': self._create_volume_mounts()} - def _add_node_selector(self): utils.set_dict_value(self.template, 'spec.template.spec.nodeSelector', @@ -118,22 +153,6 @@ class KubernetesObject(object): return {'name': name, type_name: type_data} - def _create_volume_mounts(self): - """Return all "volumeMounts" items per container""" - volume_mounts_items = [self._create_volume_mounts_item(vol) - for vol in self._volume_mounts] - ssh_vol = {'name': self.ssh_key, - 'mountPath': self.SSH_MOUNT_PATH} - volume_mounts_items.append(self._create_volume_mounts_item(ssh_vol)) - return volume_mounts_items - - @staticmethod - def _create_volume_mounts_item(volume_mount): - """Create a "volumeMounts" item""" - return {'name': volume_mount['name'], - 'mountPath': volume_mount['mountPath'], - 'readOnly': volume_mount.get('readOnly', False)} - class ServiceObject(object): diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py index 21a12a0d3..4323c026a 100644 --- a/yardstick/tests/unit/orchestrator/test_kubernetes.py +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -110,6 +110,36 @@ service ssh restart;while true ; do sleep 10000; done'] class KubernetesObjectTestCase(base.BaseUnitTestCase): + def test__init_one_container(self): + pod_name = 'pod_name' + _kwargs = {'args': ['arg1', 'arg2'], + 'image': 'fake_image', + 'command': 'fake_command'} + k8s_obj = kubernetes.KubernetesObject(pod_name, **_kwargs) + self.assertEqual(1, len(k8s_obj._containers)) + container = k8s_obj._containers[0] + self.assertEqual(['arg1', 'arg2'], container._args) + self.assertEqual('fake_image', container._image) + self.assertEqual(['fake_command'], container._command) + self.assertEqual([], container._volume_mounts) + + def test__init_multipe_containers(self): + pod_name = 'pod_name' + containers = [] + for i in range(5): + containers.append({'args': ['arg1', 'arg2'], + 'image': 'fake_image_%s' % i, + 'command': 'fake_command_%s' % i}) + _kwargs = {'containers': containers} + k8s_obj = kubernetes.KubernetesObject(pod_name, **_kwargs) + self.assertEqual(5, len(k8s_obj._containers)) + for i in range(5): + container = k8s_obj._containers[i] + self.assertEqual(['arg1', 'arg2'], container._args) + self.assertEqual('fake_image_%s' % i, container._image) + self.assertEqual(['fake_command_%s' % i], container._command) + self.assertEqual([], container._volume_mounts) + def test__add_volumes(self): volume1 = {'name': 'fake_sshkey', 'configMap': {'name': 'fake_sshkey'}} @@ -150,26 +180,29 @@ class KubernetesObjectTestCase(base.BaseUnitTestCase): with self.assertRaises(exceptions.KubernetesTemplateInvalidVolumeType): kubernetes.KubernetesObject._create_volume_item(volume) + +class ContainerObjectTestCase(base.BaseUnitTestCase): + def test__create_volume_mounts(self): volume_mount = {'name': 'fake_name', 'mountPath': 'fake_path'} - ssh_vol = {'name': kubernetes.KubernetesObject.SSHKEY_DEFAULT, - 'mountPath': kubernetes.KubernetesObject.SSH_MOUNT_PATH, + ssh_vol = {'name': 'fake_ssh_key', + 'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH, 'readOnly': False} expected = copy.deepcopy(volume_mount) expected['readOnly'] = False expected = [expected, ssh_vol] - k8s_obj = kubernetes.KubernetesObject('name', - volumeMounts=[volume_mount]) - output = k8s_obj._create_volume_mounts() + container_obj = kubernetes.ContainerObject( + 'cname', 'fake_ssh_key', volumeMounts=[volume_mount]) + output = container_obj._create_volume_mounts() self.assertEqual(expected, output) def test__create_volume_mounts_no_volume_mounts(self): - ssh_vol = {'name': kubernetes.KubernetesObject.SSHKEY_DEFAULT, - 'mountPath': kubernetes.KubernetesObject.SSH_MOUNT_PATH, + ssh_vol = {'name': 'fake_ssh_key2', + 'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH, 'readOnly': False} - k8s_obj = kubernetes.KubernetesObject('name') - output = k8s_obj._create_volume_mounts() + container_obj = kubernetes.ContainerObject('name', 'fake_ssh_key2') + output = container_obj._create_volume_mounts() self.assertEqual([ssh_vol], output) def test__create_volume_mounts_item(self): @@ -177,20 +210,20 @@ class KubernetesObjectTestCase(base.BaseUnitTestCase): 'mountPath': 'fake_path'} expected = copy.deepcopy(volume_mount) expected['readOnly'] = False - output = kubernetes.KubernetesObject._create_volume_mounts_item( + output = kubernetes.ContainerObject._create_volume_mounts_item( volume_mount) self.assertEqual(expected, output) - def test__create_container_item(self): + def test_get_container_item(self): volume_mount = {'name': 'fake_name', 'mountPath': 'fake_path'} args = ['arg1', 'arg2'] - k8s_obj = kubernetes.KubernetesObject( + container_obj = kubernetes.ContainerObject( 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], args=args) expected = {'args': args, - 'command': [kubernetes.KubernetesObject.COMMAND_DEFAULT], - 'image': kubernetes.KubernetesObject.IMAGE_DEFAULT, + 'command': [kubernetes.ContainerObject.COMMAND_DEFAULT], + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', - 'volumeMounts': k8s_obj._create_volume_mounts()} - self.assertEqual(expected, k8s_obj._create_container_item()) + 'volumeMounts': container_obj._create_volume_mounts()} + self.assertEqual(expected, container_obj.get_container_item()) -- cgit 1.2.3-korg