diff --git a/salt/modules/ebuild.py b/salt/modules/ebuild.py index ea41656eaf..5295c2652c 100644 --- a/salt/modules/ebuild.py +++ b/salt/modules/ebuild.py @@ -75,9 +75,20 @@ def _porttree(): def _p_to_cp(p): - ret = _porttree().dbapi.xmatch("match-all", p) - if ret: - return portage.cpv_getkey(ret[0]) + try: + ret = portage.dep_getkey(p) + if ret: + return ret + except portage.exception.InvalidAtom: + pass + + try: + ret = _porttree().dbapi.xmatch('bestmatch-visible', p) + if ret: + return portage.dep_getkey(ret) + except portage.exception.InvalidAtom: + pass + return None @@ -91,11 +102,14 @@ def _allnodes(): def _cpv_to_cp(cpv): - ret = portage.cpv_getkey(cpv) - if ret: - return ret - else: - return cpv + try: + ret = portage.dep_getkey(cpv) + if ret: + return ret + except portage.exception.InvalidAtom: + pass + + return cpv def _cpv_to_version(cpv): diff --git a/salt/modules/portage_config.py b/salt/modules/portage_config.py index 2a05ebb451..8441956bc3 100644 --- a/salt/modules/portage_config.py +++ b/salt/modules/portage_config.py @@ -75,6 +75,8 @@ def _get_config_file(conf, atom): if parts.cp == '*/*': # parts.repo will be empty if there is no repo part relative_path = parts.repo or "gentoo" + elif str(parts.cp).endswith('/*'): + relative_path = str(parts.cp).split("/")[0] + "_" else: relative_path = os.path.join(*[x for x in os.path.split(parts.cp) if x != '*']) else: @@ -92,9 +94,20 @@ def _p_to_cp(p): Convert a package name or a DEPEND atom to category/package format. Raises an exception if program name is ambiguous. ''' - ret = _porttree().dbapi.xmatch("match-all", p) - if ret: - return portage.cpv_getkey(ret[0]) + try: + ret = portage.dep_getkey(p) + if ret: + return ret + except portage.exception.InvalidAtom: + pass + + try: + ret = _porttree().dbapi.xmatch('bestmatch-visible', p) + if ret: + return portage.dep_getkey(ret) + except portage.exception.InvalidAtom: + pass + return None @@ -188,12 +201,7 @@ def _package_conf_file_to_dir(file_name): else: os.rename(path, path + '.tmpbak') os.mkdir(path, 0o755) - with salt.utils.files.fopen(path + '.tmpbak') as fh_: - for line in fh_: - line = line.strip() - if line and not line.startswith('#'): - append_to_package_conf(file_name, string=line) - os.remove(path + '.tmpbak') + os.rename(path + '.tmpbak', os.path.join(path, 'tmp')) return True else: os.mkdir(path, 0o755) @@ -218,7 +226,7 @@ def _package_conf_ordering(conf, clean=True, keep_backup=False): shutil.copy(file_path, file_path + '.bak') backup_files.append(file_path + '.bak') - if cp[0] == '/' or cp.split('/') > 2: + if cp[0] == '/' or len(cp.split('/')) > 2: with salt.utils.files.fopen(file_path) as fp_: rearrange.extend(fp_.readlines()) os.remove(file_path) diff --git a/tests/unit/modules/test_portage_config.py b/tests/unit/modules/test_portage_config.py index a0d0119954..bdff520f45 100644 --- a/tests/unit/modules/test_portage_config.py +++ b/tests/unit/modules/test_portage_config.py @@ -7,11 +7,14 @@ ''' # Import Python libs from __future__ import absolute_import +import re # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.paths import TMP +import salt.utils.files # Import salt libs import salt.modules.portage_config as portage_config @@ -19,27 +22,104 @@ import salt.modules.portage_config as portage_config @skipIf(NO_MOCK, NO_MOCK_REASON) class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): - class DummyAtom(object): - def __init__(self, atom): - self.cp, self.repo = atom.split("::") if "::" in atom else (atom, None) + + + def __init__(self): + self.cp = None + self.repo = None + + def __call__(self, atom, *_, **__): + if atom == '#' or isinstance(atom, MagicMock): + self.repo = None + self.cp = None + return self + + # extract (and remove) repo + atom, self.repo = atom.split('::') if '::' in atom else (atom, None) + + # remove '>, >=, <=, =, ~' etc. + atom = re.sub('[<>~+=]', '', atom) + # remove slots + atom = re.sub(':[0-9][^:]*', '', atom) + # remove version + atom = re.sub('-[0-9][\.0-9]*', '', atom) + + self.cp = atom + return self def setup_loader_modules(self): - self.portage = MagicMock() - self.addCleanup(delattr, self, 'portage') - return {portage_config: {'portage': self.portage}} + try: + import portage + return {} + except: + dummy_atom = self.DummyAtom() + self.portage = MagicMock() + self.portage.dep.Atom = MagicMock(side_effect=dummy_atom) + self.portage.dep_getkey = MagicMock(side_effect=lambda x: dummy_atom(x).cp) + self.portage.exception.InvalidAtom = Exception + self.addCleanup(delattr, self, 'portage') + return {portage_config: {'portage': self.portage}} def test_get_config_file_wildcards(self): pairs = [ ('*/*::repo', '/etc/portage/package.mask/repo'), ('*/pkg::repo', '/etc/portage/package.mask/pkg'), - ('cat/*', '/etc/portage/package.mask/cat'), + ('cat/*', '/etc/portage/package.mask/cat_'), ('cat/pkg', '/etc/portage/package.mask/cat/pkg'), ('cat/pkg::repo', '/etc/portage/package.mask/cat/pkg'), ] for (atom, expected) in pairs: - dummy_atom = self.DummyAtom(atom) - self.portage.dep.Atom = MagicMock(return_value=dummy_atom) - with patch.object(portage_config, '_p_to_cp', MagicMock(return_value=dummy_atom.cp)): - self.assertEqual(portage_config._get_config_file('mask', atom), expected) + self.assertEqual(portage_config._get_config_file('mask', atom), expected) + + def test_enforce_nice_config(self): + atoms = [ + ('*/*::repo', 'repo'), + ('*/pkg1::repo', 'pkg1'), + ('cat/*', 'cat_'), + ('cat/pkg2', 'cat/pkg2'), + ('cat/pkg3::repo', 'cat/pkg3'), + ('cat/pkg5-0.0.0.0:0', 'cat/pkg5'), + ('>cat/pkg6-0.0.0.0:0::repo', 'cat/pkg6'), + ('<=cat/pkg7-0.0.0.0', 'cat/pkg7'), + ('=cat/pkg8-0.0.0.0', 'cat/pkg8'), + ] + + supported = [ + ('accept_keywords', ['~amd64']), + ('env', ['glibc.conf']), + ('license', ['LICENCE1', 'LICENCE2']), + ('mask', ['']), + ('properties', ['* -interactive']), + ('unmask', ['']), + ('use', ['apple', '-banana', 'ananas', 'orange']), + ] + + base_path = TMP + '/package.{0}' + + def make_line(atom, addition): + return atom + (' ' + addition if addition != '' else '') + '\n' + + for typ, additions in supported: + path = base_path.format(typ) + with salt.utils.files.fopen(path, 'a') as fh: + for atom, _ in atoms: + for addition in additions: + line = make_line(atom, addition) + fh.write('# comment for: ' + line) + fh.write(line) + + with patch.object(portage_config, 'BASE_PATH', base_path): + with patch.object(portage_config, '_merge_flags', lambda l1, l2, _: list(set(l1 + l2))): + portage_config.enforce_nice_config() + + for typ, additions in supported: + for atom, file_name in atoms: + with salt.utils.files.fopen(base_path.format(typ) + "/" + file_name, 'r') as fh: + for line in fh: + self.assertTrue(atom in line, msg="'{}' not in '{}'".format(addition, line)) + for addition in additions: + self.assertTrue(addition in line, msg="'{}' not in '{}'".format(addition, line)) +