diff --git a/argo-poem-tools.spec b/argo-poem-tools.spec index d774052..de75735 100644 --- a/argo-poem-tools.spec +++ b/argo-poem-tools.spec @@ -2,7 +2,7 @@ Summary: Script installs packages on ARGO mon boxes. Name: argo-poem-tools -Version: 0.2.7 +Version: 0.3.0 Release: 1%{?dist} Source0: %{name}-%{version}.tar.gz License: ASL 2.0 @@ -52,6 +52,8 @@ rm -rf $RPM_BUILD_ROOT %attr(0755,root,root) %dir %{_localstatedir}/log/argo-poem-tools/ %changelog +* Mon Jun 17 2024 Katarina Zailac - 0.3.7-1%{?dist} +- ARGO-4661 Make tool multi-tenant aware * Thu Apr 4 2024 Katarina Zailac - 0.2.7-1%{?dist} - ARGO-4502 Generalize method for fetching distro name * Thu Aug 3 2023 Katarina Zailac - 0.2.6-1%{?dist} diff --git a/config/argo-poem-tools.conf b/config/argo-poem-tools.conf index 5f60494..f916acb 100644 --- a/config/argo-poem-tools.conf +++ b/config/argo-poem-tools.conf @@ -1,6 +1,9 @@ -[GENERAL] -Host = egi.tenant.com +[tenant1] +Host = tenant1.example.com Token = some-token-1234 - -[PROFILES] MetricProfiles = TEST_PROFILE1, TEST_PROFILE2 + +[tenant2] +Host = tenant2.example.com +Token = some-token-5678 +MetricProfiles = TEST_PROFILE3, TEST_PROFILE4 diff --git a/exec/argo-poem-packages.py b/exec/argo-poem-packages.py index c6552af..998e7d2 100755 --- a/exec/argo-poem-packages.py +++ b/exec/argo-poem-packages.py @@ -1,6 +1,5 @@ #!/usr/bin/python3 import argparse -import configparser import logging import logging.handlers import subprocess @@ -8,7 +7,10 @@ import requests from argo_poem_tools.config import Config -from argo_poem_tools.packages import Packages, PackageException +from argo_poem_tools.exceptions import ConfigException, PackageException, \ + POEMException, MergingException +from argo_poem_tools.packages import Packages +from argo_poem_tools.poem import POEM, merge_tenants_data from argo_poem_tools.repos import YUMRepos LOGFILE = "/var/log/argo-poem-tools/argo-poem-tools.log" @@ -63,95 +65,84 @@ def main(): subprocess.call(['yum', 'clean', 'all']) config = Config() - token = config.get_token() - hostname = config.get_hostname() - profiles = config.get_profiles() + tenants_configurations = config.get_configuration() - logger.info( - 'Sending request for profile(s): ' + ', '.join(profiles) - ) - if backup_repos: - repos = YUMRepos( - hostname=hostname, token=token, profiles=profiles, - override=False + tenant_repos = dict() + for tenant, configuration in tenants_configurations.items(): + logger.info( + f"{tenant}: Sending request for profile(s): " + f"{', '.join(configuration['metricprofiles'])}" ) - else: - repos = YUMRepos(hostname=hostname, token=token, profiles=profiles) - - data = repos.get_data(include_internal=include_internal) - - if not data: - logger.warning( - 'No data for given metric profile(s): ' + - ', '.join(profiles) + poem = POEM( + hostname=configuration["host"], + token=configuration["token"], + profiles=configuration["metricprofiles"] ) - sys.exit(2) - - else: - logger.info('Creating YUM repo files...') - files = repos.create_file(include_internal=include_internal) + tenant_repos.update({ + tenant: poem.get_data(include_internal=include_internal) + }) - logger.info('Created files: ' + '; '.join(files)) + data = merge_tenants_data(tenant_repos) - pkg = Packages(data) - - if noop: - info_msg, warn_msg = pkg.no_op() - - else: - info_msg, warn_msg = pkg.install() - - # if there were repo files backed up, now they are restored - repos.clean() + if backup_repos: + repos = YUMRepos(data=data, override=False) - if info_msg: - for msg in info_msg: - logger.info(msg) + else: + repos = YUMRepos(data=data) - if warn_msg: - for msg in warn_msg: - logger.warning(msg) + logger.info("Creating YUM repo files...") - sys.exit(1) + files = repos.create_file(include_internal=include_internal) - else: - missing_packages_msg = '' - if repos.missing_packages: - missing_packages_msg = \ - 'Missing packages for given distro: ' + \ - ', '.join(repos.missing_packages) - logger.warning(missing_packages_msg) + logger.info(f"Created files: {'; '.join(files)}") - if not noop: - if missing_packages_msg: - print('WARNING: ' + missing_packages_msg) + pkg = Packages(data) - logger.info("The run finished successfully.") - sys.exit(0) + if noop: + info_msg, warn_msg = pkg.no_op() - except requests.exceptions.ConnectionError as err: - logger.error(err) - sys.exit(2) + else: + info_msg, warn_msg = pkg.install() - except requests.exceptions.RequestException as err: - logger.error(err) - sys.exit(2) + # if there were repo files backed up, now they are restored + repos.clean() - except configparser.ParsingError as err: - logger.error(err) - sys.exit(2) + if info_msg: + for msg in info_msg: + logger.info(msg) - except configparser.NoSectionError as err: - logger.error(err) - sys.exit(2) + if warn_msg: + for msg in warn_msg: + logger.warning(msg) - except configparser.NoOptionError as err: - logger.error(err) - sys.exit(2) + sys.exit(1) - except PackageException as err: + else: + missing_packages_msg = '' + if repos.missing_packages: + missing_packages_msg = ( + f"Missing packages for given distro: " + f"{', '.join(repos.missing_packages)}" + ) + logger.warning(missing_packages_msg) + + if not noop: + if missing_packages_msg: + print(f"WARNING: {missing_packages_msg}") + + logger.info("The run finished successfully.") + sys.exit(0) + + except ( + requests.exceptions.ConnectionError, + requests.exceptions.RequestException, + ConfigException, + POEMException, + MergingException, + PackageException + ) as err: logger.error(err) sys.exit(2) diff --git a/modules/config.py b/modules/config.py index d351c3b..376602f 100644 --- a/modules/config.py +++ b/modules/config.py @@ -1,28 +1,59 @@ import configparser -conf = '/etc/argo-poem-tools/argo-poem-tools.conf' +from argo_poem_tools.exceptions import ConfigException class Config: - def __init__(self): - self.conf = conf + def __init__(self, file="/etc/argo-poem-tools/argo-poem-tools.conf"): + self.file = file + self.conf = self._read() + self.tenants = self._get_tenants() - def read(self): + def _check_file_exists(self): + conf = configparser.ConfigParser() + try: + with open(self.file) as f: + conf.read_file(f) + + except IOError: + raise ConfigException(f"File {self.file} does not exist") + + def _read(self): config = configparser.ConfigParser() - config.read(self.conf) + config.read(self.file) return config - def get_hostname(self): - config = self.read() - return config.get('GENERAL', 'host') + def _get_tenants(self): + tenants = list() + for section in self.conf.sections(): + if section != "GENERAL": + tenants.append(section) + + return tenants + + def get_configuration(self): + configuration = dict() + for tenant in self.tenants: + for entry in ["host", "token", "metricprofiles"]: + try: + if entry == "metricprofiles": + profiles_string = self.conf.get(tenant, entry) + value = [ + p.strip() for p in profiles_string.split(',') + ] + + else: + value = self.conf.get(tenant, entry) + + if tenant in configuration: + configuration[tenant].update({entry: value}) - def get_token(self): - config = self.read() - return config.get('GENERAL', 'token') + else: + configuration.update({tenant: {entry: value}}) - def get_profiles(self): - config = self.read() - profiles_string = config.get('PROFILES', 'metricprofiles') - profiles = [p.strip() for p in profiles_string.split(',')] + except configparser.NoOptionError: + raise ConfigException( + f"Missing '{entry}' entry for tenant '{tenant}'" + ) - return profiles + return configuration diff --git a/modules/exceptions.py b/modules/exceptions.py new file mode 100644 index 0000000..5f8fb06 --- /dev/null +++ b/modules/exceptions.py @@ -0,0 +1,25 @@ +class MyException(Exception): + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return str(self.msg) + + +class ConfigException(MyException): + def __str__(self): + return f"Configuration file error: {str(self.msg)}" + + +class PackageException(MyException): + pass + + +class POEMException(MyException): + def __str__(self): + return f"Error fetching YUM repos: {str(self.msg)}" + + +class MergingException(MyException): + def __str__(self): + return f"Error merging POEM data: {str(self.msg)}" diff --git a/modules/packages.py b/modules/packages.py index 79afc60..2085d8a 100644 --- a/modules/packages.py +++ b/modules/packages.py @@ -1,6 +1,7 @@ import subprocess from re import compile +from argo_poem_tools.exceptions import PackageException _rpm_re = compile('(\S+)-(?:(\d*):)?(.*)-(~?\w+[\w.]*)') @@ -69,10 +70,6 @@ def _compare_vr(vr1, vr2): return _compare_versions(v1, v2) -class PackageException(Exception): - pass - - class Packages: def __init__(self, data): self.data = data @@ -455,9 +452,7 @@ def install(self): except Exception as e: self._failsafe_lock_versions() - raise PackageException('Error installing packages: {}'.format( - str(e)) - ) + raise PackageException(f"Error installing packages: {str(e)}") def no_op(self): try: @@ -520,9 +515,7 @@ def no_op(self): except Exception as e: self._failsafe_lock_versions() - raise PackageException( - 'Error analysing packages: {}'.format(str(e)) - ) + raise PackageException(f"Error analysing packages: {str(e)}") def _lock_versions(self): self._get_locked_versions() diff --git a/modules/poem.py b/modules/poem.py new file mode 100644 index 0000000..4b4ea07 --- /dev/null +++ b/modules/poem.py @@ -0,0 +1,160 @@ +import subprocess + +import requests +from argo_poem_tools.exceptions import POEMException, MergingException + + +def merge_tenants_data(data): + merged_data = dict() + + for tenant, repos in data.items(): + for name, info in repos.items(): + if name not in merged_data: + merged_data.update({name: info}) + + else: + if name == "missing_packages": + incoming_missing = set(repos[name]) + existing_missing = set(merged_data[name]) + merged_data["missing_packages"] = sorted( + list(incoming_missing.union(existing_missing)) + ) + + else: + existing_packages = merged_data[name]["packages"] + existing_names = [ + item["name"] for item in existing_packages + ] + + for package in info["packages"]: + if package["name"] not in existing_names: + existing_packages.append(package) + + if ( + package["name"] in existing_names and + package not in existing_packages + ): + raise MergingException( + f"Package '{package['name']}' must be the same " + f"version across all tenants" + ) + + merged_data[name]["packages"] = sorted( + existing_packages, key=lambda p: p["name"] + ) + + return merged_data + + +class POEM: + def __init__(self, hostname, token, profiles): + self.hostname = hostname + self.token = token + self.profiles = profiles + self.missing_packages = None + + @staticmethod + def _get_os(): + string = subprocess.check_output(["cat", "/etc/os-release"]) + + string = string.decode('utf-8') + + string_list = string.split("\n") + + name = [ + line.split("=")[1].lower().split(" ")[0].replace('"', "") for line + in string_list if line.startswith("NAME") + ][0] + + version = [ + line.split("=")[1].split(".")[0].replace('"', "") for + line in string_list if line.startswith("VERSION_ID") + ][0] + + return f"{name}{version}" + + def _build_url(self, include_internal=False): + hostname = self.hostname + if hostname.startswith('https://'): + hostname = hostname[8:] + + if hostname.startswith('http://'): + hostname = hostname[7:] + + if hostname.endswith('/'): + hostname = hostname[0:-1] + + if include_internal: + repos = "repos_internal" + + else: + repos = "repos" + + return f"https://{hostname}/api/v2/{repos}/{self._get_os()}" + + def _refine_list_of_profiles(self): + return f"[{', '.join(self.profiles)}]" + + def get_data(self, include_internal=False): + headers = { + 'x-api-key': self.token, + 'profiles': self._refine_list_of_profiles() + } + response = requests.get(self._build_url(), headers=headers, timeout=180) + + data_internal = None + missing_packages_internal = list() + if include_internal: + response_internal = requests.get( + self._build_url(include_internal=True), + headers={"x-api-key": self.token}, + timeout=180 + ) + + if response_internal.status_code == 200: + internal_json = response_internal.json() + data_internal = internal_json["data"] + missing_packages_internal = internal_json["missing_packages"] + + else: + msg = f"{response_internal.status_code} " \ + f"{response_internal.reason}" + + try: + msg = f"{msg}: {response_internal.json()['detail']}" + + except (ValueError, TypeError, KeyError): + pass + + raise POEMException(msg) + + if response.status_code == 200: + data_json = response.json() + data = data_json["data"] + if data_internal: + for name, info in data_internal.items(): + if name in data: + p = data[name]["packages"] + info["packages"] + packages = dict((v["name"], v) for v in p).values() + data[name]["packages"] = sorted( + packages, key=lambda k: k["name"] + ) + + self.missing_packages = sorted( + list(set( + data_json['missing_packages'] + missing_packages_internal + )) + ) + + return data + + else: + msg = f"{response.status_code} {response.reason}" + + try: + msg = f"{msg}: {response.json()['detail']}" + + except (ValueError, TypeError, KeyError): + pass + + raise POEMException(msg) diff --git a/modules/repos.py b/modules/repos.py index 768f355..f293f4e 100644 --- a/modules/repos.py +++ b/modules/repos.py @@ -2,86 +2,15 @@ import shutil import subprocess -import requests - class YUMRepos: - def __init__( - self, hostname, token, profiles, repos_path='/etc/yum.repos.d', - override=True - ): - self.hostname = hostname - self.token = token - self.profiles = profiles + def __init__(self, data, repos_path='/etc/yum.repos.d', override=True): + self.data = data self.path = repos_path self.override = override - self.data = None self.missing_packages = None - def get_data(self, include_internal=False): - headers = { - 'x-api-key': self.token, - 'profiles': self._refine_list_of_profiles() - } - response = requests.get(self._build_url(), headers=headers, timeout=180) - - data_internal = None - missing_packages_internal = list() - if include_internal: - response_internal = requests.get( - self._build_url(include_internal=True), - headers={"x-api-key": self.token}, - timeout=180 - ) - - if response_internal.status_code == 200: - internal_json = response_internal.json() - data_internal = internal_json["data"] - missing_packages_internal = internal_json["missing_packages"] - - else: - try: - msg = response_internal.json()["detail"] - - except (ValueError, TypeError, KeyError): - msg = f"{response_internal.status_code} " \ - f"{response_internal.reason}" - - raise requests.exceptions.RequestException(msg) - - if response.status_code == 200: - data_json = response.json() - data = data_json["data"] - if data_internal: - for name, info in data_internal.items(): - if name in data: - p = data[name]["packages"] + info["packages"] - packages = dict((v["name"], v) for v in p).values() - data[name]["packages"] = sorted( - packages, key=lambda k: k["name"] - ) - - self.missing_packages = sorted( - list(set( - data_json['missing_packages'] + missing_packages_internal - )) - ) - - return data - - else: - try: - msg = response.json()['detail'] - - except (ValueError, TypeError, KeyError): - msg = '%s %s' % (response.status_code, response.reason) - - raise requests.exceptions.RequestException(msg) - def create_file(self, include_internal=False): - if not self.data: - self.data = self.get_data(include_internal=include_internal) - files = [] for key, value in self.data.items(): title = key @@ -113,45 +42,3 @@ def clean(self): shutil.rmtree(tmp_dir) subprocess.call(['yum', 'clean', 'all']) - - @classmethod - def _get_centos_version(cls): - string = subprocess.check_output(["cat", "/etc/os-release"]) - - string = string.decode('utf-8') - - string_list = string.split("\n") - - name = [ - line.split("=")[1].lower().split(" ")[0].replace('"', "") for line - in string_list if line.startswith("NAME") - ][0] - - version = [ - line.split("=")[1].split(".")[0].replace('"', "") for - line in string_list if line.startswith("VERSION_ID") - ][0] - - return f"{name}{version}" - - def _build_url(self, include_internal=False): - hostname = self.hostname - if hostname.startswith('https://'): - hostname = hostname[8:] - - if hostname.startswith('http://'): - hostname = hostname[7:] - - if hostname.endswith('/'): - hostname = hostname[0:-1] - - if include_internal: - repos = "repos_internal" - - else: - repos = "repos" - - return f"https://{hostname}/api/v2/{repos}/{self._get_centos_version()}" - - def _refine_list_of_profiles(self): - return '[' + ', '.join(self.profiles) + ']' diff --git a/tests/test_config.py b/tests/test_config.py index 3b31146..036dabd 100755 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,29 +1,55 @@ -import configparser import os import unittest -from argo_poem_tools.config import Config +from argo_poem_tools.config import Config, ConfigException mock_file_name = 'mock-file.conf' -file_ok = '[GENERAL]\nHost = egi.tenant.com\nToken = some-token-1234\n\n' \ - '[PROFILES]\nMetricProfiles = TEST_PROFILE1, TEST_PROFILE2' - -file_missing_general = '[PROFILES]\nMetricProfiles = TEST_PROFILE1, ' \ - 'TEST_PROFILE2' - -file_missing_host = '[GENERAL]\nToken = some-token-1234\n\n' \ - '[PROFILES]\nMetricProfiles = TEST_PROFILE1, TEST_PROFILE2' - -file_missing_token = '[GENERAL]\nHost = egi.tenant.com\n\n' \ - '[PROFILES]\nMetricProfiles = TEST_PROFILE1, TEST_PROFILE2' - -file_missing_profiles = '[GENERAL]\nHost = egi.tenant.com\n' \ - 'Token = some-token-1234' - -file_missing_mp = '[GENERAL]\nHost = egi.tenant.com\nToken = some-token-1234' \ - '\n\n[PROFILES]\nSomeProfiles = TEST_PROFILE1, TEST_PROFILE2' +file_ok = """ +[tenant1] +Host = tenant1.example.com +Token = some-token-1234 +MetricProfiles = TEST_PROFILE1, TEST_PROFILE2 + +[tenant2] +Host = tenant2.example.com +Token = some-token-5678 +MetricProfiles = TEST_PROFILE3, TEST_PROFILE4 +""" + +file_missing_host = """ +[tenant1] +Token = some-token-1234 +MetricProfiles = TEST_PROFILE1, TEST_PROFILE2 + +[tenant2] +Host = tenant2.example.com +Token = some-token-5678 +MetricProfiles = TEST_PROFILE3, TEST_PROFILE4 +""" + +file_missing_token = """ +[tenant1] +Host = tenant1.example.com +Token = some-token-1234 +MetricProfiles = TEST_PROFILE1, TEST_PROFILE2 + +[tenant2] +Host = tenant2.example.com +MetricProfiles = TEST_PROFILE3, TEST_PROFILE4 +""" + +file_missing_profiles = """ +[tenant1] +Host = tenant1.example.com +Token = some-token-1234 + +[tenant2] +Host = tenant2.example.com +Token = some-token-5678 +MetricProfiles = TEST_PROFILE3, TEST_PROFILE4 +""" class ConfigTests(unittest.TestCase): @@ -31,109 +57,68 @@ def tearDown(self): if os.path.isfile(mock_file_name): os.remove(mock_file_name) - def test_read(self): - with open(mock_file_name, 'w') as f: - f.write(file_ok) - - config = Config() - config.conf = mock_file_name - - self.assertTrue(config.read()) - - def test_get_hostname(self): + def test_get_configuration(self): with open(mock_file_name, 'w') as f: f.write(file_ok) - config = Config() - config.conf = mock_file_name - self.assertEqual(config.get_hostname(), 'egi.tenant.com') - - def test_get_hostname_no_section(self): - with open(mock_file_name, 'w') as f: - f.write(file_missing_general) - - config = Config() - config.conf = mock_file_name - self.assertRaises( - configparser.NoSectionError, - config.get_hostname + config = Config(file=mock_file_name) + self.assertEqual( + config.get_configuration(), { + "tenant1": { + "host": "tenant1.example.com", + "token": "some-token-1234", + "metricprofiles": ["TEST_PROFILE1", "TEST_PROFILE2"] + }, + "tenant2": { + "host": "tenant2.example.com", + "token": "some-token-5678", + "metricprofiles": ["TEST_PROFILE3", "TEST_PROFILE4"] + } + } ) - def test_get_hostname_no_option(self): + def test_get_configuration_no_host(self): with open(mock_file_name, 'w') as f: f.write(file_missing_host) - config = Config() - config.conf = mock_file_name - - self.assertRaises( - configparser.NoOptionError, - config.get_hostname - ) - - def test_get_token(self): - with open(mock_file_name, 'w') as f: - f.write(file_ok) - - config = Config() - config.conf = mock_file_name - self.assertEqual(config.get_token(), 'some-token-1234') + config = Config(file=mock_file_name) + with self.assertRaises(ConfigException) as context: + config.get_configuration() - def test_get_token_no_section(self): - with open(mock_file_name, 'w') as f: - f.write(file_missing_general) - - config = Config() - config.conf = mock_file_name - - self.assertRaises( - configparser.NoSectionError, - config.get_token + self.assertEqual( + context.exception.__str__(), + "Configuration file error: Missing 'host' entry for tenant " + "'tenant1'" ) - def test_get_token_no_option(self): + def test_get_configuration_token_missing(self): with open(mock_file_name, 'w') as f: f.write(file_missing_token) - config = Config() - config.conf = mock_file_name + config = Config(file=mock_file_name) - self.assertRaises( - configparser.NoOptionError, - config.get_token - ) - - def test_get_profiles(self): - with open(mock_file_name, 'w') as f: - f.write(file_ok) - - config = Config() - config.conf = mock_file_name + with self.assertRaises(ConfigException) as context: + config.get_configuration() self.assertEqual( - config.get_profiles(), ['TEST_PROFILE1', 'TEST_PROFILE2'] + context.exception.__str__(), + "Configuration file error: Missing 'token' entry for tenant " + "'tenant2'" ) - def test_get_profiles_no_section(self): + def test_get_configuration_profiles_missing(self): with open(mock_file_name, 'w') as f: f.write(file_missing_profiles) - config = Config() - config.conf = mock_file_name - self.assertRaises( - configparser.NoSectionError, - config.get_profiles - ) + config = Config(file=mock_file_name) - def test_get_profiles_no_option(self): - with open(mock_file_name, 'w') as f: - f.write(file_missing_mp) + with self.assertRaises(ConfigException) as context: + config.get_configuration() - config = Config() - config.conf = mock_file_name - self.assertRaises( - configparser.NoOptionError, - config.get_profiles + self.assertEqual( + context.exception.__str__(), + "Configuration file error: Missing 'metricprofiles' entry for " + "tenant 'tenant1'" ) diff --git a/tests/test_packages.py b/tests/test_packages.py index 756e15f..88f7a96 100755 --- a/tests/test_packages.py +++ b/tests/test_packages.py @@ -2,8 +2,8 @@ import unittest from unittest import mock -from argo_poem_tools.packages import Packages, _compare_versions, _compare_vr, \ - PackageException +from argo_poem_tools.exceptions import PackageException +from argo_poem_tools.packages import Packages, _compare_versions, _compare_vr data = { "argo-devel": { @@ -751,8 +751,12 @@ def test_install_lock_versions_if_exception( mock_get.side_effect = mock_func_exception mock_check_call.side_effect = mock_func mock_lock.side_effect = mock_func - with self.assertRaises(PackageException): + with self.assertRaises(PackageException) as context: self.pkgs.install() + self.assertEqual( + context.exception.__str__(), + "Error installing packages: An error." + ) self.assertFalse(mock_check_call.called) self.assertEqual(mock_lock.call_count, 1) @@ -923,8 +927,12 @@ def test_no_op_lock_versions_if_exception( mock_get.side_effect = mock_func_exception mock_check_call.side_effect = mock_func mock_lock.side_effect = mock_func - with self.assertRaises(PackageException): + with self.assertRaises(PackageException) as context: self.pkgs.no_op() + self.assertEqual( + context.exception.__str__(), + "Error analysing packages: An error." + ) self.assertFalse(mock_check_call.called) self.assertEqual(mock_lock.call_count, 1) diff --git a/tests/test_poem.py b/tests/test_poem.py new file mode 100644 index 0000000..f8e2a20 --- /dev/null +++ b/tests/test_poem.py @@ -0,0 +1,884 @@ +import unittest +from unittest import mock + +from argo_poem_tools.exceptions import POEMException, MergingException +from argo_poem_tools.poem import POEM, merge_tenants_data + +mock_data = { + "data": { + "argo-devel": { + "content": "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" + "devel/centos6/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=\n", + "packages": [ + { + "name": "nagios-plugins-fedcloud", + "version": "0.5.0" + }, + { + "name": "nagios-plugins-globus", + "version": "0.1.5" + }, + { + "name": "nagios-plugins-igtf", + "version": "1.4.0" + }, + ] + }, + "nordugrid-updates": { + "content": "[nordugrid-updates]\n" + "name=NorduGrid - $basearch - Updates\n" + "baseurl=http://download.nordugrid.org/repos/6" + "/centos/el6/$basearch/updates\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" + "nordugrid-6\n" + "priority=1\n" + "exclude=ca_*\n", + "packages": [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + } + }, + "missing_packages": [ + "nagios-plugins-bdii (1.0.14)", + "nagios-plugins-egi-notebooks (0.2.3)" + ] +} + +mock_data_internal_metrics = { + "data": { + "argo-devel": { + "content": "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" + "devel/centos6/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=\n", + "packages": [ + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-argo-tools", + "version": "0.1.1" + }, + { + "name": "argo-probe-oidc", + "version": "present" + }, + { + "name": "nagios-plugins-igtf", + "version": "1.4.0" + } + ] + } + }, + "missing_packages": [] +} + +OS_RELEASE_EL7 = \ + b'NAME="CentOS Linux"\n' \ + b'VERSION="7 (Core)"\n' \ + b'ID="centos"\n' \ + b'ID_LIKE="rhel fedora"\n' \ + b'VERSION_ID="7"\n' \ + b'PRETTY_NAME="CentOS Linux 7 (Core)"\n' \ + b'ANSI_COLOR="0;31"\n' \ + b'CPE_NAME="cpe:/o:centos:centos:7"\n' \ + b'HOME_URL="https://www.centos.org/"\n' \ + b'BUG_REPORT_URL="https://bugs.centos.org/"\n\n' \ + b'CENTOS_MANTISBT_PROJECT="CentOS-7"\n' \ + b'CENTOS_MANTISBT_PROJECT_VERSION="7"\n' \ + b'REDHAT_SUPPORT_PRODUCT="centos"\n' \ + b'REDHAT_SUPPORT_PRODUCT_VERSION="7"\n\n' + +OS_RELEASE_EL9 = \ + b'NAME="Rocky Linux"\n' \ + b'VERSION="9.1 (Blue Onyx)"\n' \ + b'ID="rocky"\n' \ + b'ID_LIKE="rhel centos fedora"\n' \ + b'VERSION_ID="9.1"\n' \ + b'PLATFORM_ID="platform:el9"\n' \ + b'PRETTY_NAME="Rocky Linux 9.1 (Blue Onyx)"\n' \ + b'ANSI_COLOR="0;32"\n' \ + b'LOGO="fedora-logo-icon"\n' \ + b'CPE_NAME="cpe:/o:rocky:rocky:9::baseos"\n' \ + b'HOME_URL="https://rockylinux.org/"\n' \ + b'BUG_REPORT_URL="https://bugs.rockylinux.org/"\n' \ + b'ROCKY_SUPPORT_PRODUCT="Rocky-Linux-9"\n' \ + b'ROCKY_SUPPORT_PRODUCT_VERSION="9.1"\n' \ + b'REDHAT_SUPPORT_PRODUCT="Rocky Linux"\n' \ + b'REDHAT_SUPPORT_PRODUCT_VERSION="9.1"\n' + + +class MockResponse: + def __init__(self, dat, status_code): + self.data = dat + self.status_code = status_code + if status_code == 404: + self.reason = 'Not Found' + + elif status_code == 403: + self.reason = 'Forbidden' + + elif status_code == 400: + self.reason = 'Bad Request' + + elif status_code == 500: + self.reason = 'Server Error' + + def json(self): + return self.data + + +def mock_request_ok(*args, **kwargs): + if "repos_internal" in args[0]: + return MockResponse(mock_data_internal_metrics, 200) + + else: + return MockResponse(mock_data, 200) + + +def mock_request_wrong_url(*args, **kwargs): + return MockResponse( + '

Not Found

\n' + '

The requested resource was not found on this server.

', 404 + ) + + +def mock_request_wrong_token(*args, **kwargs): + return MockResponse( + {"detail": "Authentication credentials were not provided."}, 403 + ) + + +def mock_request_wrong_profiles(*args, **kwargs): + return MockResponse( + {"detail": "You must define profile!"}, 400 + ) + + +def mock_request_server_error(*args, **kwargs): + return MockResponse('

Server Error (500)

', 500) + + +def mock_request_json_without_details_key(*args, **kwargs): + return MockResponse( + {'error': 'Your error message is not correct'}, 400 + ) + + +class MergeDataTests(unittest.TestCase): + def setUp(self): + self.argo_content = ( + "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/devel/rocky9/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=" + ) + self.epel_content = ( + "[epel]\nname=Extra Packages for Enterprise " + "Linux 9 - $basearch\n#baseurl=" + "http://download.fedoraproject.org/pub/epel" + "/9/$basearch\n" + "mirrorlist=https://mirrors.fedoraproject.org/" + "metalink?repo=epel-9&arch=$basearch\n" + "failovermethod=priority\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=https://dl.fedoraproject.org/pub/epel" + "/RPM-GPG-KEY-EPEL-7\n" + "priority=11" + ) + self.nordugrid_content = ( + "[nordugrid-updates]\n" + "name=NorduGrid - $basearch - Updates\n" + "baseurl=http://download.nordugrid.org/repos/6" + "/centos/el6/$basearch/updates\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" + "nordugrid-6\n" + "priority=1\n" + "exclude=ca_*\n" + ) + packages1 = [ + { + "name": "argo-probe-argo-tools", + "version": "0.2.0" + }, + { + "name": "argo-probe-cert", + "version": "2.0.1" + }, + { + "name": "argo-probe-ams", + "version": "present" + }, + { + "name": "argo-probe-poem", + "version": "present" + } + ] + packages2 = [ + { + "name": "nagios-plugins-http", + "version": "present" + }, + { + "name": "nagios-plugins-disk", + "version": "present" + }, + { + "name": "nagios-plugins-procs", + "version": "present" + } + ] + packages3 = [ + { + "name": "argo-probe-igtf", + "version": "2.1.0" + }, + { + "name": "argo-probe-cert", + "version": "2.0.1" + }, + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-poem", + "version": "present" + } + ] + packages4 = [ + { + "name": "nagios-plugins-dummy", + "version": "present" + }, + { + "name": "nagios-plugins-procs", + "version": "present" + } + ] + packages5 = [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + packages6 = [ + { + "name": "argo-probe-argo-tools", + "version": "0.2.1" + }, + { + "name": "argo-probe-poem", + "version": "present" + } + ] + self.data_ok = { + "tenant1": { + "argo": { + "content": self.argo_content, + "packages": packages1 + }, + "epel": { + "content": self.epel_content, + "packages": packages2 + }, + "missing_packages": [ + "argo-probe-grnet-agora (0.4)", + "nagios-plugins-egi-notebooks (0.2.3)" + ] + }, + "tenant2": { + "argo": { + "content": self.argo_content, + "packages": packages3 + }, + "epel": { + "content": self.epel_content, + "packages": packages4 + }, + "nordugrid-updates": { + "content": self.nordugrid_content, + "packages": packages5 + }, + "missing_packages": [ + "nagios-plugins-bdii (1.0.14)", + "nagios-plugins-egi-notebooks (0.2.3)" + ] + } + + } + self.data_different_versions = { + "tenant1": { + "argo": { + "content": self.argo_content, + "packages": packages1 + }, + "missing_packages": [] + }, + "tenant2": { + "argo": { + "content": self.argo_content, + "packages": packages6 + }, + "missing_packages": [] + } + } + + def test_merge_data(self): + merged_data = merge_tenants_data(data=self.data_ok) + self.assertEqual( + merged_data, { + "argo": { + "content": self.argo_content, + "packages": [ + { + "name": "argo-probe-ams", + "version": "present" + }, + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-argo-tools", + "version": "0.2.0" + }, + { + "name": "argo-probe-cert", + "version": "2.0.1" + }, + { + "name": "argo-probe-igtf", + "version": "2.1.0" + }, + { + "name": "argo-probe-poem", + "version": "present" + } + ] + }, + "epel": { + "content": self.epel_content, + "packages": [ + { + "name": "nagios-plugins-disk", + "version": "present" + }, + { + "name": "nagios-plugins-dummy", + "version": "present" + }, + { + "name": "nagios-plugins-http", + "version": "present" + }, + { + "name": "nagios-plugins-procs", + "version": "present" + } + ] + }, + "nordugrid-updates": { + "content": self.nordugrid_content, + "packages": [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + }, + "missing_packages": [ + "argo-probe-grnet-agora (0.4)", + "nagios-plugins-bdii (1.0.14)", + "nagios-plugins-egi-notebooks (0.2.3)" + ] + } + ) + + def test_merge_data_with_different_versions(self): + with self.assertRaises(MergingException) as context: + merge_tenants_data(data=self.data_different_versions) + self.assertEqual( + context.exception.__str__(), + "Error merging POEM data: Package 'argo-probe-argo-tools' must be " + "the same version across all tenants" + ) + + +class POEMTests(unittest.TestCase): + def setUp(self): + self.poem1 = POEM( + hostname='mock.url.com', + token='some-token-1234', + profiles=['TEST_PROFILE1', 'TEST_PROFILE2'] + ) + self.poem2 = POEM( + hostname='http://mock.url.com/', + token='some-token-1234', + profiles=['TEST_PROFILE1'] + ) + self.poem3 = POEM( + hostname='https://mock.url.com/', + token='some-token-1234', + profiles=['TEST_PROFILE1', 'TEST_PROFILE2'] + ) + self.poem4 = POEM( + hostname='mock.url.com', + token='some-token-1234', + profiles='' + ) + self.poem5 = POEM( + hostname='mock.url.com', + token='some-token-1234', + profiles=['TEST_PROFILE1', 'TEST_PROFILE2'] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_el7(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL7 + data = self.poem1.get_data() + mock_request.assert_called_once_with( + 'https://mock.url.com/api/v2/repos/centos7', + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, + timeout=180 + ) + self.assertEqual(data, mock_data['data']) + self.assertEqual( + self.poem1.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_el9(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem1.get_data() + mock_request.assert_called_once_with( + 'https://mock.url.com/api/v2/repos/rocky9', + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, + timeout=180 + ) + self.assertEqual(data, mock_data['data']) + self.assertEqual( + self.poem1.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_including_internal_metrics(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem1.get_data(include_internal=True) + self.assertEqual(mock_request.call_count, 2) + mock_request.assert_has_calls([ + mock.call( + "https://mock.url.com/api/v2/repos/rocky9", + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, + timeout=180 + ), + mock.call( + "https://mock.url.com/api/v2/repos_internal/rocky9", + headers={'x-api-key': 'some-token-1234'}, + timeout=180 + ) + ], any_order=True) + self.assertEqual( + data, { + "argo-devel": { + "content": "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" + "devel/centos6/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=\n", + "packages": [ + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-argo-tools", + "version": "0.1.1" + }, + { + "name": "argo-probe-oidc", + "version": "present" + }, + { + "name": "nagios-plugins-fedcloud", + "version": "0.5.0" + }, + { + "name": "nagios-plugins-globus", + "version": "0.1.5" + }, + { + "name": "nagios-plugins-igtf", + "version": "1.4.0" + } + ] + }, + "nordugrid-updates": { + "content": "[nordugrid-updates]\n" + "name=NorduGrid - $basearch - Updates\n" + "baseurl=http://download.nordugrid.org/repos/6" + "/centos/el6/$basearch/updates\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" + "nordugrid-6\n" + "priority=1\n" + "exclude=ca_*\n", + "packages": [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + } + } + ) + self.assertEqual( + self.poem1.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_hostname_http(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem2.get_data() + mock_request.assert_called_once_with( + 'https://mock.url.com/api/v2/repos/rocky9', + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1]'}, + timeout=180 + ) + self.assertEqual(data, mock_data['data']) + self.assertEqual( + self.poem2.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_hostname_http_including_internal( + self, mock_request, mock_sp + ): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem2.get_data(include_internal=True) + mock_request.assert_has_calls([ + mock.call( + "https://mock.url.com/api/v2/repos/rocky9", + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1]'}, + timeout=180 + ), + mock.call( + "https://mock.url.com/api/v2/repos_internal/rocky9", + headers={'x-api-key': 'some-token-1234'}, + timeout=180 + ) + ], any_order=True) + self.assertEqual( + data, { + "argo-devel": { + "content": "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" + "devel/centos6/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=\n", + "packages": [ + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-argo-tools", + "version": "0.1.1" + }, + { + "name": "argo-probe-oidc", + "version": "present" + }, + { + "name": "nagios-plugins-fedcloud", + "version": "0.5.0" + }, + { + "name": "nagios-plugins-globus", + "version": "0.1.5" + }, + { + "name": "nagios-plugins-igtf", + "version": "1.4.0" + } + ] + }, + "nordugrid-updates": { + "content": "[nordugrid-updates]\n" + "name=NorduGrid - $basearch - Updates\n" + "baseurl=http://download.nordugrid.org/repos/6" + "/centos/el6/$basearch/updates\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" + "nordugrid-6\n" + "priority=1\n" + "exclude=ca_*\n", + "packages": [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + } + } + ) + self.assertEqual( + self.poem2.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_hostname_https(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem3.get_data() + mock_request.assert_called_once_with( + 'https://mock.url.com/api/v2/repos/rocky9', + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, + timeout=180 + ) + self.assertEqual(data, mock_data['data']) + self.assertEqual( + self.poem3.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_hostname_https_including_internal( + self, mock_request, mock_sp + ): + mock_request.side_effect = mock_request_ok + mock_sp.return_value = OS_RELEASE_EL9 + data = self.poem3.get_data(include_internal=True) + mock_request.assert_has_calls([ + mock.call( + "https://mock.url.com/api/v2/repos/rocky9", + headers={'x-api-key': 'some-token-1234', + 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, + timeout=180 + ), + mock.call( + "https://mock.url.com/api/v2/repos_internal/rocky9", + headers={'x-api-key': 'some-token-1234'}, + timeout=180 + ) + ], any_order=True) + self.assertEqual( + data, { + "argo-devel": { + "content": "[argo-devel]\n" + "name=ARGO Product Repository\n" + "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" + "devel/centos6/\n" + "gpgcheck=0\n" + "enabled=1\n" + "priority=99\n" + "exclude=\n" + "includepkgs=\n", + "packages": [ + { + "name": "argo-probe-ams-publisher", + "version": "present" + }, + { + "name": "argo-probe-argo-tools", + "version": "0.1.1" + }, + { + "name": "argo-probe-oidc", + "version": "present" + }, + { + "name": "nagios-plugins-fedcloud", + "version": "0.5.0" + }, + { + "name": "nagios-plugins-globus", + "version": "0.1.5" + }, + { + "name": "nagios-plugins-igtf", + "version": "1.4.0" + } + ] + }, + "nordugrid-updates": { + "content": "[nordugrid-updates]\n" + "name=NorduGrid - $basearch - Updates\n" + "baseurl=http://download.nordugrid.org/repos/6" + "/centos/el6/$basearch/updates\n" + "enabled=1\n" + "gpgcheck=1\n" + "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" + "nordugrid-6\n" + "priority=1\n" + "exclude=ca_*\n", + "packages": [ + { + "name": "nordugrid-arc-nagios-plugins", + "version": "2.0.0" + } + ] + } + } + ) + self.assertEqual( + self.poem3.missing_packages, + [ + 'nagios-plugins-bdii (1.0.14)', + 'nagios-plugins-egi-notebooks (0.2.3)' + ] + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_server_error(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_server_error + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data() + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 500 Server Error" + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_server_error_including_internal( + self, mock_request, mock_sp + ): + mock_request.side_effect = mock_request_server_error + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data(include_internal=True) + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 500 Server Error" + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_wrong_url(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_wrong_url + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data() + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 404 Not Found" + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_wrong_token(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_wrong_token + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data() + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 403 Forbidden: " + "Authentication credentials were not provided." + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_no_profiles(self, mock_request, mock_sp): + mock_request.side_effect = mock_request_wrong_profiles + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data() + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 400 Bad Request: " + "You must define profile!" + ) + + @mock.patch('argo_poem_tools.poem.subprocess.check_output') + @mock.patch('argo_poem_tools.poem.requests.get') + def test_get_data_if_json_without_details( + self, mock_request, mock_sp + ): + mock_request.side_effect = mock_request_json_without_details_key + mock_sp.return_value = OS_RELEASE_EL9 + with self.assertRaises(POEMException) as err: + self.poem1.get_data() + self.assertEqual( + err.exception.__str__(), + "Error fetching YUM repos: 400 Bad Request" + ) diff --git a/tests/test_repo.py b/tests/test_repo.py index b9b2355..a6b20f2 100755 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -2,217 +2,19 @@ import unittest from unittest import mock -import requests from argo_poem_tools.repos import YUMRepos -mock_data = { - "data": { - "argo-devel": { - "content": "[argo-devel]\n" - "name=ARGO Product Repository\n" - "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" - "devel/centos6/\n" - "gpgcheck=0\n" - "enabled=1\n" - "priority=99\n" - "exclude=\n" - "includepkgs=\n", - "packages": [ - { - "name": "nagios-plugins-fedcloud", - "version": "0.5.0" - }, - { - "name": "nagios-plugins-globus", - "version": "0.1.5" - }, - { - "name": "nagios-plugins-igtf", - "version": "1.4.0" - }, - ] - }, - "nordugrid-updates": { - "content": "[nordugrid-updates]\n" - "name=NorduGrid - $basearch - Updates\n" - "baseurl=http://download.nordugrid.org/repos/6" - "/centos/el6/$basearch/updates\n" - "enabled=1\n" - "gpgcheck=1\n" - "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" - "nordugrid-6\n" - "priority=1\n" - "exclude=ca_*\n", - "packages": [ - { - "name": "nordugrid-arc-nagios-plugins", - "version": "2.0.0" - } - ] - } - }, - "missing_packages": [ - "nagios-plugins-bdii (1.0.14)", - "nagios-plugins-egi-notebooks (0.2.3)"] -} - -mock_data_internal_metrics = { - "data": { - "argo-devel": { - "content": "[argo-devel]\n" - "name=ARGO Product Repository\n" - "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" - "devel/centos6/\n" - "gpgcheck=0\n" - "enabled=1\n" - "priority=99\n" - "exclude=\n" - "includepkgs=\n", - "packages": [ - { - "name": "argo-probe-ams-publisher", - "version": "present" - }, - { - "name": "argo-probe-argo-tools", - "version": "0.1.1" - }, - { - "name": "argo-probe-oidc", - "version": "present" - }, - { - "name": "nagios-plugins-igtf", - "version": "1.4.0" - } - ] - } - }, - "missing_packages": [] -} - -OS_RELEASE_EL7 = \ - b'NAME="CentOS Linux"\n' \ - b'VERSION="7 (Core)"\n' \ - b'ID="centos"\n' \ - b'ID_LIKE="rhel fedora"\n' \ - b'VERSION_ID="7"\n' \ - b'PRETTY_NAME="CentOS Linux 7 (Core)"\n' \ - b'ANSI_COLOR="0;31"\n' \ - b'CPE_NAME="cpe:/o:centos:centos:7"\n' \ - b'HOME_URL="https://www.centos.org/"\n' \ - b'BUG_REPORT_URL="https://bugs.centos.org/"\n\n' \ - b'CENTOS_MANTISBT_PROJECT="CentOS-7"\n' \ - b'CENTOS_MANTISBT_PROJECT_VERSION="7"\n' \ - b'REDHAT_SUPPORT_PRODUCT="centos"\n' \ - b'REDHAT_SUPPORT_PRODUCT_VERSION="7"\n\n' - -OS_RELEASE_EL9 = \ - b'NAME="Rocky Linux"\n' \ - b'VERSION="9.1 (Blue Onyx)"\n' \ - b'ID="rocky"\n' \ - b'ID_LIKE="rhel centos fedora"\n' \ - b'VERSION_ID="9.1"\n' \ - b'PLATFORM_ID="platform:el9"\n' \ - b'PRETTY_NAME="Rocky Linux 9.1 (Blue Onyx)"\n' \ - b'ANSI_COLOR="0;32"\n' \ - b'LOGO="fedora-logo-icon"\n' \ - b'CPE_NAME="cpe:/o:rocky:rocky:9::baseos"\n' \ - b'HOME_URL="https://rockylinux.org/"\n' \ - b'BUG_REPORT_URL="https://bugs.rockylinux.org/"\n' \ - b'ROCKY_SUPPORT_PRODUCT="Rocky-Linux-9"\n' \ - b'ROCKY_SUPPORT_PRODUCT_VERSION="9.1"\n' \ - b'REDHAT_SUPPORT_PRODUCT="Rocky Linux"\n' \ - b'REDHAT_SUPPORT_PRODUCT_VERSION="9.1"\n' - - -class MockResponse: - def __init__(self, dat, status_code): - self.data = dat - self.status_code = status_code - if status_code == 404: - self.reason = 'Not Found' - - elif status_code == 403: - self.reason = 'Forbidden' - - elif status_code == 400: - self.reason = 'Bad Request' - - elif status_code == 500: - self.reason = 'Server Error' - - def json(self): - return self.data - - -def mock_request_ok(*args, **kwargs): - if "repos_internal" in args[0]: - return MockResponse(mock_data_internal_metrics, 200) - - else: - return MockResponse(mock_data, 200) - - -def mock_request_wrong_url(*args, **kwargs): - return MockResponse( - '

Not Found

\n' - '

The requested resource was not found on this server.

', 404 - ) - - -def mock_request_wrong_token(*args, **kwargs): - return MockResponse( - {"detail": "Authentication credentials were not provided."}, 403 - ) - - -def mock_request_wrong_profiles(*args, **kwargs): - return MockResponse( - {"detail": "You must define profile!"}, 400 - ) - - -def mock_request_server_error(*args, **kwargs): - return MockResponse('

Server Error (500)

', 500) - - -def mock_request_json_without_details_key(*args, **kwargs): - return MockResponse( - {'error': 'Your error message is not correct'}, 400 - ) +from test_poem import mock_data class YUMReposTests(unittest.TestCase): def setUp(self): self.repos1 = YUMRepos( - hostname='mock.url.com', - token='some-token-1234', - profiles=['TEST_PROFILE1', 'TEST_PROFILE2'], + data=mock_data["data"], repos_path=os.getcwd() ) self.repos2 = YUMRepos( - hostname='http://mock.url.com/', - token='some-token-1234', - profiles=['TEST_PROFILE1'], - repos_path=os.getcwd() - ) - self.repos3 = YUMRepos( - hostname='https://mock.url.com/', - token='some-token-1234', - profiles=['TEST_PROFILE1', 'TEST_PROFILE2'], - repos_path=os.getcwd() - ) - self.repos4 = YUMRepos( - hostname='mock.url.com', - token='some-token-1234', - profiles='', - repos_path=os.getcwd() - ) - self.repos5 = YUMRepos( - hostname='mock.url.com', - token='some-token-1234', - profiles=['TEST_PROFILE1', 'TEST_PROFILE2'], + data=mock_data["data"], repos_path=os.getcwd(), override=False ) @@ -224,425 +26,8 @@ def tearDown(self): if os.path.exists('nordugrid-updates.repo'): os.remove('nordugrid-updates.repo') - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_el7(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL7 - data = self.repos1.get_data() - mock_request.assert_called_once_with( - 'https://mock.url.com/api/v2/repos/centos7', - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, - timeout=180 - ) - self.assertEqual(data, mock_data['data']) - self.assertEqual( - self.repos1.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_el9(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos1.get_data() - mock_request.assert_called_once_with( - 'https://mock.url.com/api/v2/repos/rocky9', - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, - timeout=180 - ) - self.assertEqual(data, mock_data['data']) - self.assertEqual( - self.repos1.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_including_internal_metrics(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos1.get_data(include_internal=True) - self.assertEqual(mock_request.call_count, 2) - mock_request.assert_has_calls([ - mock.call( - "https://mock.url.com/api/v2/repos/rocky9", - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, - timeout=180 - ), - mock.call( - "https://mock.url.com/api/v2/repos_internal/rocky9", - headers={'x-api-key': 'some-token-1234'}, - timeout=180 - ) - ], any_order=True) - self.assertEqual( - data, { - "argo-devel": { - "content": "[argo-devel]\n" - "name=ARGO Product Repository\n" - "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" - "devel/centos6/\n" - "gpgcheck=0\n" - "enabled=1\n" - "priority=99\n" - "exclude=\n" - "includepkgs=\n", - "packages": [ - { - "name": "argo-probe-ams-publisher", - "version": "present" - }, - { - "name": "argo-probe-argo-tools", - "version": "0.1.1" - }, - { - "name": "argo-probe-oidc", - "version": "present" - }, - { - "name": "nagios-plugins-fedcloud", - "version": "0.5.0" - }, - { - "name": "nagios-plugins-globus", - "version": "0.1.5" - }, - { - "name": "nagios-plugins-igtf", - "version": "1.4.0" - } - ] - }, - "nordugrid-updates": { - "content": "[nordugrid-updates]\n" - "name=NorduGrid - $basearch - Updates\n" - "baseurl=http://download.nordugrid.org/repos/6" - "/centos/el6/$basearch/updates\n" - "enabled=1\n" - "gpgcheck=1\n" - "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" - "nordugrid-6\n" - "priority=1\n" - "exclude=ca_*\n", - "packages": [ - { - "name": "nordugrid-arc-nagios-plugins", - "version": "2.0.0" - } - ] - } - } - ) - self.assertEqual( - self.repos1.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_hostname_http(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos2.get_data() - mock_request.assert_called_once_with( - 'https://mock.url.com/api/v2/repos/rocky9', - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1]'}, - timeout=180 - ) - self.assertEqual(data, mock_data['data']) - self.assertEqual( - self.repos2.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_hostname_http_including_internal( - self, mock_request, mock_sp - ): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos2.get_data(include_internal=True) - mock_request.assert_has_calls([ - mock.call( - "https://mock.url.com/api/v2/repos/rocky9", - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1]'}, - timeout=180 - ), - mock.call( - "https://mock.url.com/api/v2/repos_internal/rocky9", - headers={'x-api-key': 'some-token-1234'}, - timeout=180 - ) - ], any_order=True) - self.assertEqual( - data, { - "argo-devel": { - "content": "[argo-devel]\n" - "name=ARGO Product Repository\n" - "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" - "devel/centos6/\n" - "gpgcheck=0\n" - "enabled=1\n" - "priority=99\n" - "exclude=\n" - "includepkgs=\n", - "packages": [ - { - "name": "argo-probe-ams-publisher", - "version": "present" - }, - { - "name": "argo-probe-argo-tools", - "version": "0.1.1" - }, - { - "name": "argo-probe-oidc", - "version": "present" - }, - { - "name": "nagios-plugins-fedcloud", - "version": "0.5.0" - }, - { - "name": "nagios-plugins-globus", - "version": "0.1.5" - }, - { - "name": "nagios-plugins-igtf", - "version": "1.4.0" - } - ] - }, - "nordugrid-updates": { - "content": "[nordugrid-updates]\n" - "name=NorduGrid - $basearch - Updates\n" - "baseurl=http://download.nordugrid.org/repos/6" - "/centos/el6/$basearch/updates\n" - "enabled=1\n" - "gpgcheck=1\n" - "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" - "nordugrid-6\n" - "priority=1\n" - "exclude=ca_*\n", - "packages": [ - { - "name": "nordugrid-arc-nagios-plugins", - "version": "2.0.0" - } - ] - } - } - ) - self.assertEqual( - self.repos2.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_hostname_https(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos3.get_data() - mock_request.assert_called_once_with( - 'https://mock.url.com/api/v2/repos/rocky9', - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, - timeout=180 - ) - self.assertEqual(data, mock_data['data']) - self.assertEqual( - self.repos3.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_hostname_https_including_internal( - self, mock_request, mock_sp - ): - mock_request.side_effect = mock_request_ok - mock_sp.return_value = OS_RELEASE_EL9 - data = self.repos3.get_data(include_internal=True) - mock_request.assert_has_calls([ - mock.call( - "https://mock.url.com/api/v2/repos/rocky9", - headers={'x-api-key': 'some-token-1234', - 'profiles': '[TEST_PROFILE1, TEST_PROFILE2]'}, - timeout=180 - ), - mock.call( - "https://mock.url.com/api/v2/repos_internal/rocky9", - headers={'x-api-key': 'some-token-1234'}, - timeout=180 - ) - ], any_order=True) - self.assertEqual( - data, { - "argo-devel": { - "content": "[argo-devel]\n" - "name=ARGO Product Repository\n" - "baseurl=http://rpm-repo.argo.grnet.gr/ARGO/" - "devel/centos6/\n" - "gpgcheck=0\n" - "enabled=1\n" - "priority=99\n" - "exclude=\n" - "includepkgs=\n", - "packages": [ - { - "name": "argo-probe-ams-publisher", - "version": "present" - }, - { - "name": "argo-probe-argo-tools", - "version": "0.1.1" - }, - { - "name": "argo-probe-oidc", - "version": "present" - }, - { - "name": "nagios-plugins-fedcloud", - "version": "0.5.0" - }, - { - "name": "nagios-plugins-globus", - "version": "0.1.5" - }, - { - "name": "nagios-plugins-igtf", - "version": "1.4.0" - } - ] - }, - "nordugrid-updates": { - "content": "[nordugrid-updates]\n" - "name=NorduGrid - $basearch - Updates\n" - "baseurl=http://download.nordugrid.org/repos/6" - "/centos/el6/$basearch/updates\n" - "enabled=1\n" - "gpgcheck=1\n" - "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-" - "nordugrid-6\n" - "priority=1\n" - "exclude=ca_*\n", - "packages": [ - { - "name": "nordugrid-arc-nagios-plugins", - "version": "2.0.0" - } - ] - } - } - ) - self.assertEqual( - self.repos3.missing_packages, - [ - 'nagios-plugins-bdii (1.0.14)', - 'nagios-plugins-egi-notebooks (0.2.3)' - ] - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_server_error(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_server_error - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data() - self.assertEqual(err, '500 Server Error') - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_server_error_including_internal( - self, mock_request, mock_sp - ): - mock_request.side_effect = mock_request_server_error - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data(include_internal=True) - self.assertEqual(err, '500 Server Error') - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_wrong_url(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_wrong_url - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data() - self.assertEqual(err, '404 Not Found') - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_wrong_token(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_wrong_token - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data() - self.assertEqual( - err, '403 Forbidden: Authentication credentials were not ' - 'provided.' - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_no_profiles(self, mock_request, mock_sp): - mock_request.side_effect = mock_request_wrong_profiles - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data() - self.assertEqual( - err, '400 Bad Request: You must define profile!' - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.requests.get') - def test_get_data_if_json_without_details( - self, mock_request, mock_sp - ): - mock_request.side_effect = mock_request_json_without_details_key - mock_sp.return_value = OS_RELEASE_EL9 - with self.assertRaises(requests.exceptions.RequestException) as err: - self.repos1.get_data() - self.assertEqual( - err, '400 Bad Request' - ) - - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') - def test_create_file(self, mock_get_data, mock_sp): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 + def test_create_file(self): files = self.repos1.create_file() - mock_get_data.assert_called_once_with(include_internal=False) self.assertEqual( files, [os.path.join(os.getcwd(), 'argo-devel.repo'), @@ -664,13 +49,8 @@ def test_create_file(self, mock_get_data, mock_sp): content2, mock_data['data']['nordugrid-updates']['content'] ) - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') - def test_create_file_including_internal(self, mock_get_data, mock_sp): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 + def test_create_file_including_internal(self): files = self.repos1.create_file(include_internal=True) - mock_get_data.assert_called_once_with(include_internal=True) self.assertEqual( files, [os.path.join(os.getcwd(), 'argo-devel.repo'), @@ -695,18 +75,13 @@ def test_create_file_including_internal(self, mock_get_data, mock_sp): @mock.patch('argo_poem_tools.repos.shutil.copyfile') @mock.patch('argo_poem_tools.repos.os.path.isfile') @mock.patch('argo_poem_tools.repos.os.makedirs') - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') def test_do_override_file_which_already_exists( - self, mock_get_data, mock_sp, mock_mkdir, mock_isfile, mock_cp + self, mock_mkdir, mock_isfile, mock_cp ): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 with open('argo-devel.repo', 'w') as f: f.write('test') files = self.repos1.create_file() - mock_get_data.assert_called_once_with(include_internal=False) self.assertFalse(mock_mkdir.called) self.assertFalse(mock_isfile.called) self.assertFalse(mock_cp.called) @@ -734,18 +109,13 @@ def test_do_override_file_which_already_exists( @mock.patch('argo_poem_tools.repos.shutil.copyfile') @mock.patch('argo_poem_tools.repos.os.path.isfile') @mock.patch('argo_poem_tools.repos.os.makedirs') - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') def test_do_override_file_which_already_exists_including_internal( - self, mock_get_data, mock_sp, mock_mkdir, mock_isfile, mock_cp + self, mock_mkdir, mock_isfile, mock_cp ): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 with open('argo-devel.repo', 'w') as f: f.write('test') files = self.repos1.create_file(include_internal=True) - mock_get_data.assert_called_once_with(include_internal=True) self.assertFalse(mock_mkdir.called) self.assertFalse(mock_isfile.called) self.assertFalse(mock_cp.called) @@ -773,19 +143,14 @@ def test_do_override_file_which_already_exists_including_internal( @mock.patch('argo_poem_tools.repos.shutil.copyfile') @mock.patch('argo_poem_tools.repos.os.path.isfile') @mock.patch('argo_poem_tools.repos.os.makedirs') - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') def test_do_not_override_file_which_already_exists( - self, mock_get_data, mock_sp, mock_mkdir, mock_isfile, mock_copy + self, mock_mkdir, mock_isfile, mock_copy ): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 mock_isfile.return_value = True with open('argo-devel.repo', 'w') as f: f.write('test') - files = self.repos5.create_file() - mock_get_data.assert_called_once_with(include_internal=False) + files = self.repos2.create_file() self.assertEqual(mock_mkdir.call_count, 2) mock_mkdir.assert_called_with('/tmp' + os.getcwd(), exist_ok=True) file1 = os.path.join(os.getcwd(), 'argo-devel.repo') @@ -819,19 +184,14 @@ def test_do_not_override_file_which_already_exists( @mock.patch('argo_poem_tools.repos.shutil.copyfile') @mock.patch('argo_poem_tools.repos.os.path.isfile') @mock.patch('argo_poem_tools.repos.os.makedirs') - @mock.patch('argo_poem_tools.repos.subprocess.check_output') - @mock.patch('argo_poem_tools.repos.YUMRepos.get_data') def test_do_not_override_file_which_already_exists_including_internal( - self, mock_get_data, mock_sp, mock_mkdir, mock_isfile, mock_copy + self, mock_mkdir, mock_isfile, mock_copy ): - mock_get_data.return_value = mock_data["data"] - mock_sp.return_value = OS_RELEASE_EL9 mock_isfile.return_value = True with open('argo-devel.repo', 'w') as f: f.write('test') - files = self.repos5.create_file(include_internal=True) - mock_get_data.assert_called_once_with(include_internal=True) + files = self.repos2.create_file(include_internal=True) self.assertEqual(mock_mkdir.call_count, 2) mock_mkdir.assert_called_with('/tmp' + os.getcwd(), exist_ok=True) file1 = os.path.join(os.getcwd(), 'argo-devel.repo') @@ -878,7 +238,7 @@ def test_clean( mock_isfile.return_value = True file1 = os.path.join(os.getcwd(), 'argo-devel.repo') file2 = os.path.join(os.getcwd(), 'nordugrid-updates.repo') - self.repos5.clean() + self.repos2.clean() self.assertEqual(mock_isdir.call_count, 1) mock_isdir.assert_called_with('/tmp' + os.getcwd()) self.assertEqual(mock_ls.call_count, 1)