diff --git a/bodhi-server/bodhi/server/tasks/composer.py b/bodhi-server/bodhi/server/tasks/composer.py index c140123570..f86de6bb4d 100644 --- a/bodhi-server/bodhi/server/tasks/composer.py +++ b/bodhi-server/bodhi/server/tasks/composer.py @@ -1164,12 +1164,17 @@ def _sanity_check_repo(self): if arch == 'source': repodata = os.path.join(self.path, 'compose', 'Everything', arch, 'tree', 'repodata') - sanity_check_repodata(repodata, repo_type='source') + sanity_check_repodata(repodata, repo_type='source', drpms=False) else: repodata = os.path.join(self.path, 'compose', 'Everything', arch, 'os', 'repodata') repo_type = 'module' if self.ctype == ContentType.module else 'yum' - sanity_check_repodata(repodata, repo_type=repo_type) + # Source and module repos don't have DRPMs. + if repo_type == 'yum': + drpms = get_createrepo_config(self.compose.release).get('drpms_enabled') + else: + drpms = False + sanity_check_repodata(repodata, repo_type=repo_type, drpms=drpms) except Exception: log.exception("Repodata sanity check failed, compose thrown out") self._toss_out_repo() diff --git a/bodhi-server/bodhi/server/util.py b/bodhi-server/bodhi/server/util.py index ea27c5b0d8..d849832860 100644 --- a/bodhi-server/bodhi/server/util.py +++ b/bodhi-server/bodhi/server/util.py @@ -269,7 +269,7 @@ def get_critpath_components(collection='master', component_type='rpm', component return critpath_components -def sanity_check_repodata(myurl, repo_type): +def sanity_check_repodata(myurl, repo_type, drpms): """ Sanity check the repodata for a given repository. @@ -277,6 +277,7 @@ def sanity_check_repodata(myurl, repo_type): myurl (str): A path to a repodata directory. repo_type (str): This should be set to 'yum' for Yum repositories, 'module' for module repositories, or 'source' for source repositories. + drpms (bool): A boolean to express if DRPMs generation is enabled. Raises: RepodataException: If the repodata is not valid or does not exist. ValueError: If repo_type is not an acceptable value. @@ -311,7 +312,8 @@ def sanity_check_repodata(myurl, repo_type): reqparts = ['filelists', 'primary', 'repomd', 'updateinfo'] # Source and module repos don't have DRPMs. if repo_type == 'yum': - reqparts.append('prestodelta') + if drpms: + reqparts.append('prestodelta') reqparts.append('group') elif repo_type == 'module': reqparts.append('modules') @@ -1364,7 +1366,8 @@ def get_createrepo_config(rel): return munchify({'uinfo_comp': 'XZ', 'repodata_comp': '', 'general_comp': False, - 'zchunk': True}) + 'zchunk': True, + 'drpms_enabled': True}) if f'release.{rel.name}' in configfile.sections(): log.info(f'Using custom createrepo_c config for {rel.name}.') return munchify( @@ -1372,7 +1375,8 @@ def get_createrepo_config(rel): 'repodata_comp': configfile[f'release.{rel.name}'].get('repodata-compress-type', None), 'general_comp': configfile[f'release.{rel.name}'].getboolean('general-compress'), - 'zchunk': configfile[f'release.{rel.name}'].getboolean('zchunk') + 'zchunk': configfile[f'release.{rel.name}'].getboolean('zchunk'), + 'drpms_enabled': configfile[f'release.{rel.name}'].getboolean('drpms_enabled') } ) elif f'prefix.{rel.id_prefix}' in configfile.sections(): @@ -1382,7 +1386,8 @@ def get_createrepo_config(rel): 'repodata_comp': configfile[f'prefix.{rel.id_prefix}'].get('repodata-compress-type', None), 'general_comp': configfile[f'prefix.{rel.id_prefix}'].getboolean('general-compress'), - 'zchunk': configfile[f'prefix.{rel.id_prefix}'].getboolean('zchunk') + 'zchunk': configfile[f'prefix.{rel.id_prefix}'].getboolean('zchunk'), + 'drpms_enabled': configfile[f'prefix.{rel.id_prefix}'].getboolean('drpms_enabled') } ) else: @@ -1391,6 +1396,7 @@ def get_createrepo_config(rel): {'uinfo_comp': configfile['DEFAULT'].get('updateinfo-compress-type'), 'repodata_comp': configfile['DEFAULT'].get('repodata-compress-type', None), 'general_comp': configfile['DEFAULT'].getboolean('general-compress'), - 'zchunk': configfile['DEFAULT'].getboolean('zchunk') + 'zchunk': configfile['DEFAULT'].getboolean('zchunk'), + 'drpms_enabled': configfile['DEFAULT'].getboolean('drpms_enabled') } ) diff --git a/bodhi-server/tests/test_util.py b/bodhi-server/tests/test_util.py index de480c36ff..9d5acb39e2 100644 --- a/bodhi-server/tests/test_util.py +++ b/bodhi-server/tests/test_util.py @@ -411,7 +411,7 @@ def test_correct_yum_repo_with_xz_compress(self): base.mkmetadatadir(self.tempdir) # No exception should be raised here. - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) def test_correct_yum_repo_with_gz_compress(self): """No Exception should be raised if the repo is normal. @@ -421,7 +421,7 @@ def test_correct_yum_repo_with_gz_compress(self): base.mkmetadatadir(self.tempdir, compress_type='gz') # No exception should be raised here. - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) def test_correct_yum_repo_with_bz2_compress(self): """No Exception should be raised if the repo is normal. @@ -431,7 +431,7 @@ def test_correct_yum_repo_with_bz2_compress(self): base.mkmetadatadir(self.tempdir, compress_type='bz2') # No exception should be raised here. - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) @pytest.mark.skipif( packaging.version.parse(createrepo_c.VERSION) < packaging.version.parse('1.0.0'), @@ -445,12 +445,12 @@ def test_correct_yum_repo_with_zstd_compress(self): base.mkmetadatadir(self.tempdir, compress_type='zstd') # No exception should be raised here. - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) def test_invalid_repo_type(self): """A ValueError should be raised with invalid repo type.""" with pytest.raises(ValueError) as excinfo: - util.sanity_check_repodata("so", "wrong") + util.sanity_check_repodata("so", "wrong", drpms=True) assert str(excinfo.value) == 'repo_type must be one of module, source, or yum.' @mock.patch('bodhi.server.util.librepo') @@ -462,7 +462,7 @@ class MockException(Exception): librepo.Handle.return_value.perform.side_effect = MockException(-1, 'msg', 'general_msg') with pytest.raises(RepodataException) as excinfo: - util.sanity_check_repodata('/tmp/', 'yum') + util.sanity_check_repodata('/tmp/', 'yum', drpms=True) assert str(excinfo.value) == 'msg' def _mkmetadatadir_w_modules(self): @@ -488,7 +488,7 @@ def test_correct_module_repo(self, *args): """No Exception should be raised if the repo is a normal module repo.""" self._mkmetadatadir_w_modules() # No exception should be raised here. - util.sanity_check_repodata(self.tempdir, repo_type='module') + util.sanity_check_repodata(self.tempdir, repo_type='module', drpms=True) @mock.patch('subprocess.check_output', return_value='') def test_module_repo_no_dnf_output(self, *args): @@ -496,7 +496,7 @@ def test_module_repo_no_dnf_output(self, *args): self._mkmetadatadir_w_modules() with pytest.raises(util.RepodataException) as exc: - util.sanity_check_repodata(self.tempdir, repo_type='module') + util.sanity_check_repodata(self.tempdir, repo_type='module', drpms=True) assert str(exc.value) == \ ("DNF did not return expected output when running test!" " Test: ['module', 'list'], expected: .*, output: ") @@ -509,7 +509,7 @@ def test_updateinfo_empty_tags(self): base.mkmetadatadir(self.tempdir, updateinfo=updateinfo) with pytest.raises(util.RepodataException) as exc: - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) assert str(exc.value) == 'updateinfo.xml.gz contains empty ID tags' def test_comps_invalid_notxml(self): @@ -520,7 +520,7 @@ def test_comps_invalid_notxml(self): base.mkmetadatadir(self.tempdir, comps=comps) with pytest.raises(util.RepodataException) as exc: - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) assert str(exc.value) == 'Comps file unable to be parsed' def test_comps_invalid_nonsense(self): @@ -531,7 +531,7 @@ def test_comps_invalid_nonsense(self): base.mkmetadatadir(self.tempdir, comps=comps) with pytest.raises(util.RepodataException) as exc: - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) assert str(exc.value) == 'Comps file empty' def test_repomd_missing_updateinfo(self): @@ -548,9 +548,40 @@ def test_repomd_missing_updateinfo(self): repomd.write(repomd_path, encoding='UTF-8', xml_declaration=True) with pytest.raises(util.RepodataException) as exc: - util.sanity_check_repodata(self.tempdir, repo_type='yum') + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) assert str(exc.value) == 'Required parts not in repomd.xml: updateinfo' + def test_repomd_missing_prestodelta(self): + """If the prestodelta data tag is missing in repomd.xml, an Exception should be raised.""" + base.mkmetadatadir(self.tempdir) + repomd_path = os.path.join(self.tempdir, 'repodata', 'repomd.xml') + repomd = ElementTree.parse(repomd_path) + ElementTree.register_namespace('', 'http://linux.duke.edu/metadata/repo') + root = repomd.getroot() + for data in root.findall('{http://linux.duke.edu/metadata/repo}data'): + if data.attrib['type'] == 'prestodelta': + root.remove(data) + repomd.write(repomd_path, encoding='UTF-8', xml_declaration=True) + + with pytest.raises(util.RepodataException) as exc: + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=True) + assert str(exc.value) == 'Required parts not in repomd.xml: prestodelta' + + def test_repomd_drpms_disabled(self): + """If the prestodelta data tag is missing in a repo without DRPMs is fine.""" + base.mkmetadatadir(self.tempdir) + repomd_path = os.path.join(self.tempdir, 'repodata', 'repomd.xml') + repomd = ElementTree.parse(repomd_path) + ElementTree.register_namespace('', 'http://linux.duke.edu/metadata/repo') + root = repomd.getroot() + for data in root.findall('{http://linux.duke.edu/metadata/repo}data'): + if data.attrib['type'] == 'prestodelta': + root.remove(data) + repomd.write(repomd_path, encoding='UTF-8', xml_declaration=True) + + # No exception should be raised. + util.sanity_check_repodata(self.tempdir, repo_type='yum', drpms=False) + def test_source_true(self): """It should not fail source repos for missing prestodelta or comps.""" base.mkmetadatadir(self.tempdir) @@ -565,7 +596,7 @@ def test_source_true(self): repomd.write(repomd_path, encoding='UTF-8', xml_declaration=True) # No exception should be raised. - util.sanity_check_repodata(self.tempdir, repo_type='source') + util.sanity_check_repodata(self.tempdir, repo_type='source', drpms=True) class TestTestcaseLink(base.BasePyTestCase): diff --git a/devel/ci/integration/bodhi/createrepo_c.ini b/devel/ci/integration/bodhi/createrepo_c.ini index a5d13c73a0..71514c9cc1 100644 --- a/devel/ci/integration/bodhi/createrepo_c.ini +++ b/devel/ci/integration/bodhi/createrepo_c.ini @@ -13,6 +13,8 @@ repodata-compress-type = general-compress = False # zchunk is a boolean which enables zchunk on repodata zchunk = True +# drpms_enabled is a boolean which enables DRPMs generation +drpms_enabled = True # Here you can override default values by matching Release.prefix_id or Release.name # If a value is not overridden, those from DEFAULT are used @@ -24,3 +26,6 @@ zchunk = False [release.EPEL-8] repodata-compress-type = xz zchunk = False + +[release.F40] +drpms_enabled = False diff --git a/devel/ci/integration/bodhi/pungi.rpm.conf.j2 b/devel/ci/integration/bodhi/pungi.rpm.conf.j2 index 313db04314..f5a1c300f4 100644 --- a/devel/ci/integration/bodhi/pungi.rpm.conf.j2 +++ b/devel/ci/integration/bodhi/pungi.rpm.conf.j2 @@ -83,9 +83,15 @@ check_deps = False repoclosure_backend = 'dnf' # CREATEREPO +[% if cr_config.drpms_enabled %] createrepo_deltas = [ ('^Everything$', {'*': True}) ] +[% else %] +createrepo_deltas = [ + ('^Everything$', {'*': False}) +] +[% endif %] createrepo_database = True createrepo_extra_args = [ [% if cr_config.zchunk %]