diff --git a/pytest_tests/helpers/service_helper.py b/pytest_tests/helpers/service_helper.py index 82c7d650..38a226ab 100644 --- a/pytest_tests/helpers/service_helper.py +++ b/pytest_tests/helpers/service_helper.py @@ -126,8 +126,8 @@ class CloudVmStorageServiceHelper: ssh_client.exec(f'systemctl stop {self.STORAGE_SERVICE}') ssh_client.exec('rm -rf /srv/neofs/*') - def get_binaries_version(self) -> dict: - binaries = [ + def get_binaries_version(self, binaries: list = None) -> dict: + default_binaries = [ 'neo-go', 'neofs-adm', 'neofs-cli', @@ -139,18 +139,24 @@ class CloudVmStorageServiceHelper: 'neofs-s3-gw', 'neogo-morph-cn', ] + binaries = binaries or default_binaries version_map = {} for node_name in NEOFS_NETMAP_DICT: with _create_ssh_client(node_name) as ssh_client: for binary in binaries: - out = ssh_client.exec(f'{binary} --version').stdout - version = re.search(r'version[:\s]*(.+)', out, re.IGNORECASE) - version = version.group(1) if version else 'Unknown' - if not version_map.get(binary.upper()): - version_map[binary.upper()] = version + try: + out = ssh_client.exec(f'{binary} --version').stdout + except AssertionError as err: + logger.error(f'Can not get version for {binary} because of\n{err}') + version_map[binary] = 'Can not get version' + continue + version = re.search(r'version[:\s]*v?(.+)', out, re.IGNORECASE) + version = version.group(1).strip() if version else 'Unknown' + if not version_map.get(binary): + version_map[binary] = version else: - assert version_map[binary.upper()] == version, \ + assert version_map[binary] == version, \ f'Expected binary {binary} to have identical version on all nodes ' \ f'(mismatch on node {node_name})' return version_map diff --git a/pytest_tests/pytest.ini b/pytest_tests/pytest.ini index b154f9db..7b4180e4 100644 --- a/pytest_tests/pytest.ini +++ b/pytest_tests/pytest.ini @@ -22,3 +22,4 @@ markers = failover_panic: tests for system recovery after panic reboot of a node failover_net: tests for network failure add_nodes: add nodes to cluster + check_binaries: check neofs installed binaries versions diff --git a/pytest_tests/testsuites/conftest.py b/pytest_tests/testsuites/conftest.py index 883d06ea..1e36f881 100644 --- a/pytest_tests/testsuites/conftest.py +++ b/pytest_tests/testsuites/conftest.py @@ -61,7 +61,7 @@ def _get_binaries_version_local(binaries: list) -> dict: for binary in binaries: out = _cmd_run(f'{binary} --version') version = re.search(r'version[:\s]*(.+)', out, re.IGNORECASE) - env_out[binary.upper()] = version.group(1) if version else 'Unknown' + env_out[binary] = version.group(1).strip() if version else 'Unknown' return env_out diff --git a/pytest_tests/testsuites/services/test_binaries.py b/pytest_tests/testsuites/services/test_binaries.py new file mode 100644 index 00000000..d4741586 --- /dev/null +++ b/pytest_tests/testsuites/services/test_binaries.py @@ -0,0 +1,86 @@ +import logging +from http import HTTPStatus +from re import match + +import allure +import pytest +import requests + +from common import BIN_VERSIONS_FILE +from service_helper import get_storage_service_helper + +logger = logging.getLogger('NeoLogger') + + +@allure.title('Check binaries versions') +@pytest.mark.check_binaries +def test_binaries_versions(request): + """ + Compare binaries versions from external source (url) and deployed on servers. + """ + if not BIN_VERSIONS_FILE: + pytest.skip('File with binaries and versions was not provided') + + failed_versions = {} + environment_dir = request.config.getoption('--alluredir') + env_data = None + data_for_env = {} + + binaries_to_check = download_versions_info(BIN_VERSIONS_FILE) + + with allure.step('Get binaries versions from servers'): + helper = get_storage_service_helper() + got_versions = helper.get_binaries_version(binaries=list(binaries_to_check.keys())) + + if environment_dir: + with open(f'{environment_dir}/environment.properties', 'r') as env_file: + env_data = env_file.read() + + # compare versions from servers and file + for binary, version in binaries_to_check.items(): + if binary not in got_versions: + failed_versions[binary] = 'Can not find binary' + if got_versions[binary] != version: + failed_versions[binary] = f'Expected version {version}, found version {got_versions[binary]}' + + # if something missed in environment.properties file, let's add + if env_data and binary not in env_data: + data_for_env[binary] = got_versions[binary] + + if environment_dir and data_for_env: + add_to_environment_properties(f'{environment_dir}/environment.properties', data_for_env) + + # create clear beautiful error with aggregation info + if failed_versions: + msg = '\n'.join({f'{binary}: {error}' for binary, error in failed_versions.items()}) + raise AssertionError(f'Found binaries with unexpected versions:\n{msg}') + + +@allure.step('Download info from {url}') +def download_versions_info(url: str) -> dict: + binaries_to_version = {} + + response = requests.get(url) + + assert response.status_code == HTTPStatus.OK, \ + f'Got {response.status_code} code. Content {response.json()}' + + content = response.text + assert content, f'Expected file with content, got {response}' + + for line in content.split('\n'): + m = match('(.*)=(.*)', line) + if not m: + logger.warning(f'Could not get binary/version from {line}') + continue + bin_name, bin_version = m.group(1), m.group(2) + binaries_to_version[bin_name] = bin_version + + return binaries_to_version + + +@allure.step('Update data in environment.properties') +def add_to_environment_properties(file_path: str, env_data: dict): + with open(file_path, 'a+') as env_file: + for env, env_value in env_data.items(): + env_file.write(f'{env}={env_value}\n') diff --git a/robot/variables/common.py b/robot/variables/common.py index 4a88bd04..a909fc6c 100644 --- a/robot/variables/common.py +++ b/robot/variables/common.py @@ -112,3 +112,4 @@ NEOFS_ADM_CONFIG_PATH = os.getenv("NEOFS_ADM_CONFIG_PATH") INFRASTRUCTURE_TYPE = os.getenv("INFRASTRUCTURE_TYPE", "LOCAL_DEVENV") FREE_STORAGE = os.getenv("FREE_STORAGE", "false").lower() == "true" +BIN_VERSIONS_FILE = os.getenv("BIN_VERSIONS_FILE")