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
