Define several containers per pod in a replication controller 89/57089/8
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Sat, 5 May 2018 10:13:28 +0000 (11:13 +0100)
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Thu, 14 Jun 2018 07:15:23 +0000 (07:15 +0000)
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
            <volume type definition>   # 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
            <volume type definition>   # 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 <rodolfo.alonso.hernandez@intel.com>
yardstick/orchestrator/kubernetes.py
yardstick/tests/unit/orchestrator/test_kubernetes.py

index e05c971..8ccb988 100644 (file)
@@ -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):
 
index 21a12a0..4323c02 100644 (file)
@@ -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())