4주차 과제 - 신민경 (2)

OpenStackClient의 소스 코드에 ‘**openstack image stores info’**와 ‘openstack image import info’ 명령어에 대한 새로운 기능 테스트(Functional Test)를 추가하고, 이를 성공적으로 실행시키는 것

1. 테스트 코드 작성

  • test_image_stores_info:

    • Glance의 multi-store 기능 활성화 여부를 먼저 확인

    • 기능이 비활성화된 환경에서는 오류를 내는 대신, self.skipTest를 호출하여 테스트를 ‘건너뛰기(SKIPPED)’ 처리

    • stores info 명령어가 없는 구형 버전을 위해 stores list 명령어로 폴백(fallback)하는 기능을 포함

    • 다양한 JSON 출력 형태(list 또는 dict)를 모두 처리

  • test_image_import_info:

    • JSON 출력이 실패할 경우를 대비해, 일반 텍스트(text) 출력으로 폴백하여 결과를 검증

    • API 버전에 따라 달라질 수 있는 여러 JSON 구조를 유연하게 탐색하여 검증

# openstackclient/tests/functional/image/v2/test_image.py

from tempest.lib import exceptions

# functional test for 'openstack image stores info'
    def test_image_stores_info(self):
        def _multistore_disabled(msg: str) -> bool:
            markers = [
                'Multi Backend support not enabled',
                'Multi store is not enabled',
                'Unknown command',
                'No such command "stores"',
                '404',
            ]
            return any(m in (msg or '') for m in markers)

        try:
            out = self.openstack('image stores info -f json', parse_output=True)
        except exceptions.CommandFailed as e:
            err = e.stderr.decode('utf-8') if hasattr(e.stderr, 'decode') else str(e)
            if _multistore_disabled(err):
                self.skipTest('Glance multi-store not enabled in this environment')
            out = None

        rows = None
        if isinstance(out, list):
            rows = out
        elif isinstance(out, dict) and 'stores' in out:
            rows = out['stores']

        if rows is None:
            try:
                out = self.openstack('image stores list -f json', parse_output=True)
                rows = out if isinstance(out, list) else out.get('stores')
            except exceptions.CommandFailed as e:
                err = e.stderr.decode('utf-8') if hasattr(e.stderr, 'decode') else str(e)
                if _multistore_disabled(err):
                    self.skipTest('Glance multi-store not enabled in this environment')
                raise

        self.assertIsInstance(rows, list)
        self.assertGreaterEqual(len(rows), 1)

        first = rows[0]
        has_info_cols = all(k in first for k in ('ID', 'Default'))  # Description은 없을 수도
        has_list_cols = all(k in first for k in ('Name', 'Type')) and ('Location' in first or 'Path' in first)
        self.assertTrue(has_info_cols or has_list_cols, f'Unexpected columns: {list(first.keys())}')

    # functional test for 'openstack image import info'
    def test_image_import_info(self):
        """Verify at least one import method is advertised; accept multiple JSON shapes or text fallback."""
        try:
            out = self.openstack('image import info -f json', parse_output=True)
        except exceptions.CommandFailed:
            txt = self.openstack('image import info')
            self.assertTrue(
                any(k in txt for k in ('glance-direct', 'web-download', 'copy-image')),
                f"Unexpected output for 'image import info':\n{txt}"
            )
            return

        methods = None
        if isinstance(out, dict) and 'import-methods' in out:
            payload = out['import-methods']
            if isinstance(payload, dict):
                methods = (payload.get('value') or payload.get('values') or
                           payload.get('methods') or payload.get('supported'))
            elif isinstance(payload, (list, tuple, set)):
                methods = payload
        elif isinstance(out, (list, tuple, set)):
            methods = out 

        self.assertIsNotNone(methods, f"Couldn't find methods in output: {out}")
        if isinstance(methods, (list, tuple, set)):
            self.assertGreater(len(methods), 0, 'No import methods reported')
        else:
            self.assertTrue(str(methods).strip(), 'Empty import methods')

2. 테스트

테스트 실행은 tox 도구를 사용했으며, 이 과정에서 발생한 두 가지 주요 문제를 해결

문제 1: The specified regex doesn’t match with anything

  • 원인: 테스트 실행 도구(stestr)가 파일 시스템 경로(…/…)를 인식하지 못함

  • 해결: tox 명령어에 인자를 전달할 때, 파일 경로 대신 파이썬 모듈 경로(… …) 형식으로 수정

    • Before: tox -e functional – openstackclient/tests/functional/image/v2/test_image.py

    • After: tox -e functional – openstackclient.tests.functional.image.v2.test_image

문제 2: Cloud devstack-admin was not found

  • 원인: 테스트 환경이 OpenStack API에 접속하기 위한 인증 정보를 찾지 못함

  • 해결: ~/.config/openstack/ 디렉터리에 clouds.yaml 설정 파일을 생성하여, devstack-admin이라는 이름으로 API 접속 정보를 명시

-> 이 부분은 임시적으로 해결한 것이고, 추후 수정 필요

3. 최종 결과

위 문제들 해결 후, 테스트 통과

  • 실행 명령어:

    tox -e functional -- openstackclient.tests.functional.image.v2.test_image
    
    
  • 결과 요약:

    • 총 9개 테스트 실행

    • 8개 테스트 통과 (Passed)

    • 1개 테스트 건너뜀 (Skipped)

    • 0개 테스트 실패 (Failed)

(OCA-OpenStack) minkyoungshin@Minkyoung-MacBook-Air python-openstackclient % tox -e functional -- openstackclient.tests.functional.image.v2.test_image
functional: recreate env because changed constraint(s) removed python-cyborgclient===2.5.0 added python-cyborgclient===2.6.0
functional: remove tox env folder /Users/minkyoungshin/Desktop/OCA-OPENSTACK/python-openstackclient/.tox/functional
functional: install_deps> python -I -m pip install -r /Users/minkyoungshin/Desktop/OCA-OPENSTACK/python-openstackclient/test-requirements.txt -r /Users/minkyoungshin/Desktop/OCA-OPENSTACK/python-openstackclient/requirements.txt -c https://releases.openstack.org/constraints/upper/master
.pkg: _optional_hooks> python /Users/minkyoungshin/Desktop/OCA-OPENSTACK/OCA-OpenStack/lib/python3.12/site-packages/pyproject_api/_backend.py True pbr.build
.pkg: get_requires_for_build_editable> python /Users/minkyoungshin/Desktop/OCA-OPENSTACK/OCA-OpenStack/lib/python3.12/site-packages/pyproject_api/_backend.py True pbr.build
.pkg: build_editable> python /Users/minkyoungshin/Desktop/OCA-OPENSTACK/OCA-OpenStack/lib/python3.12/site-packages/pyproject_api/_backend.py True pbr.build
functional: install_package_deps> python -I -m pip install 'cliff>=4.8.0' 'cryptography>=2.7' 'iso8601>=0.1.11' 'openstacksdk>=4.6.0' 'osc-lib>=2.3.0' 'oslo.i18n>=3.15.3' 'pbr!=2.1.0,>=2.0.0' 'python-cinderclient>=3.3.0' 'python-keystoneclient>=3.22.0' 'requests>=2.27.0' 'stevedore>=2.0.1'
functional: install_package> python -I -m pip install --force-reinstall --no-deps /Users/minkyoungshin/Desktop/OCA-OPENSTACK/python-openstackclient/.tox/.tmp/package/58/python_openstackclient-8.1.1.dev20-0.editable-py3-none-any.whl
functional: commands[0]> stestr run openstackclient.tests.functional.image.v2.test_image
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_attributes [15.095154s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_import_info [4.194282s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_list [3.950499s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_list_with_name_filter [3.622345s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_list_with_status_filter [3.696392s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_list_with_tag_filter [3.810261s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_members [10.511878s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_set_rename [7.020149s] ... ok
{0} openstackclient.tests.functional.image.v2.test_image.ImageTests.test_image_stores_info ... SKIPPED: Glance multi-store not enabled in this environment

======
Totals
======
Ran: 9 tests in 56.4168 sec.
 - Passed: 8
 - Skipped: 1
 - Expected Fail: 0
 - Unexpected Success: 0
 - Failed: 0
Sum of execute time for each test: 56.4140 sec.

==============
Worker Balance
==============
 - Worker 0 (9 tests) => 0:00:56.416799
.pkg: _exit> python /Users/minkyoungshin/Desktop/OCA-OPENSTACK/OCA-OpenStack/lib/python3.12/site-packages/pyproject_api/_backend.py True pbr.build
  functional: OK (83.78=setup[23.94]+cmd[59.85] seconds)
  congratulations :) (84.14 seconds)

https://review.opendev.org/c/openstack/python-openstackclient/+/958908