Sahara 场景集成测试

https://blueprints.launchpad.net/sahara/+spec/scenario-integration-tests

目前 Sahara 项目没有功能性的集成测试。我们需要创建新的、更灵活的集成测试。

问题描述

当前的集成测试只能测试有限数量的测试场景和集群配置,这些配置是硬编码在测试代码中的。在许多情况下,我们需要测试各种 Sahara 集群的配置,但当前的集成测试没有这个功能。此外,我们的测试文件中有大量复制粘贴的代码,这需要大量的工作来进行重构。

提议的变更

建议创建新的集成测试,使其更灵活。测试场景将定义在 YAML 文件中,预计这种方法将提供更大的灵活性。典型的场景将如下所示

credentials:
    os_username: dev-user
    os_password: swordfish
    os_tenant: devs
    os_auth_url: http://os_host:5000/v2.0
    sahara_url: http://sahara_host:8336/v1.1  # optional

network:
    type: neutron  # or nova-network
    private_network: private  # for neutron
    auto_assignment_floating_ip: false  # for nova-network
    public_network: public  # or floating_ip_pool for nova-network

clusters:
    - plugin_name: vanilla
      plugin_version: 2.6.0
      image: some_id
      node_group_templates:  # optional
        - name: master
          node_processes:
            - namenode
            - resourcemanager
          flavor_id: '3'
        - name: worker
          node_processes:
            - datanode
            - nodemanager
          flavor_id: '3'
      cluster_template:  # optional
          name: vanilla
          node_group_templates:
              master: 1
              worker: 3
      scenario:  # optional
          - run_jobs
          - scale
          - run_jobs

    - plugin_name: hdp
      plugin_version: 2.0.6
      image: some_id

最小场景将如下所示

clusters:
    - plugin_name: vanilla
      plugin_version: 2.6.0
      image: some_id

完整场景将如下所示

concurrency: 3

credentials:
    os_username: dev-user
    os_password: swordfish
    os_tenant: devs
    os_auth_url: http://os_host:5000/v2.0
    sahara_url: http://sahara_host:8336/v1.1  # optional

network:
    type: neutron  # or nova-network
    private_network: private  # for neutron
    auto_assignment_floating_ip: false  # for nova-network
    public_network: public  # or floating_ip_pool for nova-network

clusters:
      # required
    - plugin_name: vanilla
      # required
      plugin_version: 2.6.0
      # required (id or name)
      image: some_id
      node_group_templates:  # optional
        - name: master
          node_processes:
            - namenode
            - resourcemanager
          flavor_id: '3'
          description: >-
              Some description
          volumes_per_node: 2
          volumes_size: 2
          node_configs:
              HDFS:
                  dfs.datanode.du.reserved: 10
          security_groups: ~
          auto_security_group: true
          availability_zone: nova
          volumes_availability_zone: nova
          volume_type: lvm
        - name: worker
          node_processes:
            - datanode
            - nodemanager
          flavor_id: 3
      cluster_template:  # optional
          name: vanilla
          description: >-
              Some description
          cluster_configs:
              HDFS:
                  dfs.replication: 1
          node_group_templates:
              master: 1
              worker: 3
          anti_affinity: true
      cluster:
          name: test-cluster
          is_transient: true
          description: >-
              Cluster description
      scaling:
          - operation: resize
            node_group: worker
            size: 4
          - operation: add
            node_group: worker
            size: 2
      scenario:  # optional
          - run_jobs
          - scale
          - run_jobs
      edp_jobs_flow: example
      retain_resource: true  # optional

edp_jobs_flow:
    example:
      - type: Pig
        main_lib:
            source: swift
            path: path_to_pig_script.pig
        input_datasource:
            type: swift
            source: etc/edp-examples/edp-pig/top-todoers/data/input
        output_datasource:
            type: hdfs
            destination: /user/hadoop/edp-output
        configs:
            dfs.replication: 1
      - type: Java
        additional_libs:
            - type: database
              source: |
                  etc/edp-examples/.../hadoop-mapreduce-examples-2.4.1.jar
        configs:
            edp.java.main_class: |
                org.apache.hadoop.examples.QuasiMonteCarlo
        args:
            - 10
            - 10

在 YAML 文件中描述了测试场景后,我们像往常一样运行测试。Python 测试代码将从这些 YAML 文件生成。

我们将使用 Mako 库来生成 Python 代码。生成的代码将如下所示

from sahara.tests.scenario import base


class vanilla2_4_1TestCase(base.BaseTestCase):
    @classmethod
    def setUpClass(cls):
        super(vanilla2_4_1TestCase, cls).setUpClass()
        cls.credentials = {
            'os_username': 'dev-user',
            'os_password': 'swordfish',
            'os_tenant': 'devs',
            'os_auth_url': 'http://172.18.168.5:5000/v2.0',
            'sahara_url': None
        }
        cls.network = {
            'type': 'neutron',
            'public_network': 'net04_ext',
            'auto_assignment_floating_ip': False,
            'private_network': 'dev-network'
        }
        cls.testcase = {
            'image': 'sahara-juno-vanilla-2.4.1-ubuntu-14.04',
            'plugin_name': 'vanilla',
            'retain_resources': False,
            'class_name': 'vanilla2_4_1',
            'edp_jobs_flow': [
                {
                    'configs': {
                        'dfs.replication': 1
                    },
                    'output_datasource': {
                        'type': 'hdfs',
                        'destination': '/user/hadoop/edp-output'
                    },
                    'input_datasource': {
                        'type': 'swift',
                        'source':
                        'etc/edp-examples/edp-pig/top-todoers/data/input'
                    },
                    'main_lib': {
                        'type': 'swift',
                        'source':
                        'etc/edp-examples/edp-pig/top-todoers/example.pig'
                    },
                    'type': 'Pig'
                },
                {
                    'type': 'Java',
                    'args': [10, 10],
                    'additional_libs': [
                        {
                            'type': 'database',
                            'source':
                            'etc/edp-examples/hadoop2/edp-java/'
                            'hadoop-mapreduce-examples-2.4.1.jar'
                        }
                    ],
                    'configs': {
                        'edp.java.main_class':
                        'org.apache.hadoop.examples.QuasiMonteCarlo'
                    }
                }
            ],
            'scenario': ['run_jobs', 'scale', 'run_jobs'],
            'plugin_version': '2.4.1'
        }

    def test_plugin(self):
        self.create_cluster()
        self.check_run_jobs()
        self.check_scale()
        self.check_run_jobs()


class hdp2_0_6TestCase(base.BaseTestCase):
    @classmethod
    def setUpClass(cls):
        super(hdp2_0_6TestCase, cls).setUpClass()
        cls.credentials = {
            'os_username': 'dev-user',
            'os_password': 'swordfish',
            'os_tenant': 'devs',
            'os_auth_url': 'http://172.18.168.5:5000/v2.0',
            'sahara_url': None
        }
        cls.network = {
            'type': 'neutron',
            'public_network': 'net04_ext',
            'auto_assignment_floating_ip': False,
            'private_network': 'dev-network'
        }
        cls.testcase = {
            'image': 'f3c4a228-9ba4-41f1-b100-a0587689d4dd',
            'plugin_name': 'hdp',
            'retain_resources': False,
            'class_name': 'hdp2_0_6',
            'edp_jobs_flow': None,
            'scenario': ['run_jobs', 'scale', 'run_jobs'],
            'scaling': [
                {
                    'operation': 'resize',
                    'size': 5,
                    'node_group': 'worker'
                }
            ],
            'plugin_version': '2.0.6'
        }

    def test_plugin(self):
        self.create_cluster()
        self.check_run_jobs()
        self.check_scale()
        self.check_run_jobs()

Mako 模板将如下所示

from sahara.tests.scenario import base

% for testcase in testcases:
    ${make_testcase(testcase)}
% endfor

<%def name="make_testcase(testcase)">
class ${testcase['class_name']}TestCase(base.BaseTestCase):
    @classmethod
    def setUpClass(cls):
        super(${testcase['class_name']}TestCase, cls).setUpClass()
        cls.credentials = ${credentials}
        cls.network = ${network}
        cls.testcase = ${testcase}

    def test_plugin(self):
        self.create_cluster()
    % for check in testcase['scenario']:
        self.check_${check}()
    % endfor
</%def>

默认情况下,并发数将等于 CPU 核心数。此值可以在 YAML 文件中更改。

一旦完全实现,我们将把新的集成测试用于 CI。

替代方案

我们可以继续使用当前的集成测试来进一步测试 Sahara,但它们对 Sahara 用例的覆盖范围不足。

数据模型影响

REST API 影响

其他最终用户影响

部署者影响

开发者影响

开发者将能够更好地测试他们在 Sahara 中的更改。

Sahara-image-elements impact

Sahara-dashboard / Horizon 影响

实现

负责人

主要负责人

sreshetniak

其他贡献者

ylobankov slukjanov

工作项

工作内容如下

  • 在 sahara/tests 中添加 Python 代码

  • 添加测试场景示例

  • 添加新的集成测试文档

依赖项

Mako tempest-lib

测试

文档影响

我们需要在 Sahara 文档中添加关于新测试的说明。

参考资料