Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rhel-10.0: Override releasever_{major,minor} with system-release provides #2208

Open
wants to merge 6 commits into
base: rhel-10.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions dnf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,14 @@ def _setup_default_conf():
conf = dnf.conf.Conf()
subst = conf.substitutions
if 'releasever' not in subst:
subst['releasever'] = \
dnf.rpm.detect_releasever(conf.installroot)
releasever, major, minor = \
dnf.rpm.detect_releasevers(conf.installroot)
subst['releasever'] = releasever
if major is not None:
subst['releasever_major'] = major
if minor is not None:
subst['releasever_minor'] = minor

return conf

def _setup_modular_excludes(self):
Expand Down
24 changes: 18 additions & 6 deletions dnf/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ def configure(self, args, option_parser=None):
dnf.conf.PRIO_DEFAULT)
self.demands.cacheonly = True
self.base.conf._configure_from_options(opts)
self._read_conf_file(opts.releasever)
self._read_conf_file(opts.releasever, opts.releasever_major, opts.releasever_minor)
if 'arch' in opts:
self.base.conf.arch = opts.arch
self.base.conf._adjust_conf_options()
Expand Down Expand Up @@ -945,7 +945,7 @@ def configure(self, args, option_parser=None):
)
)

def _read_conf_file(self, releasever=None):
def _read_conf_file(self, releasever=None, releasever_major=None, releasever_minor=None):
timer = dnf.logging.Timer('config')
conf = self.base.conf

Expand All @@ -971,13 +971,25 @@ def _read_conf_file(self, releasever=None):
from_root = "/"
subst = conf.substitutions
subst.update_from_etc(from_root, varsdir=conf._get_value('varsdir'))

# cachedir, logs, releasever, and gpgkey are taken from or stored in installroot

det_major = None
det_minor = None
if releasever is None and conf.releasever is None:
releasever = dnf.rpm.detect_releasever(conf.installroot)
releasever, det_major, det_minor = dnf.rpm.detect_releasevers(conf.installroot)
elif releasever == '/':
releasever = dnf.rpm.detect_releasever(releasever)
if releasever is not None:
conf.releasever = releasever
releasever, det_major, det_minor = dnf.rpm.detect_releasevers(releasever)

def or_else(*args):
for arg in args:
if arg is not None:
return arg
return None
conf.releasever = or_else(releasever, conf.releasever)
conf.releasever_major = or_else(releasever_major, det_major, conf.releasever_major)
conf.releasever_minor = or_else(releasever_minor, det_minor, conf.releasever_minor)

if conf.releasever is None:
logger.warning(_("Unable to detect release version (use '--releasever' to specify "
"release version)"))
Expand Down
6 changes: 6 additions & 0 deletions dnf/cli/option_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ def _add_general_options(self):
general_grp.add_argument("--releasever", default=None,
help=_("override the value of $releasever"
" in config and repo files"))
general_grp.add_argument("--releasever-major", default=None,
help=_("override the value of $releasever_major"
" in config and repo files"))
general_grp.add_argument("--releasever-minor", default=None,
help=_("override the value of $releasever_minor"
" in config and repo files"))
general_grp.add_argument("--setopt", dest="setopts", default=[],
action=self._SetoptsCallback,
help=_("set arbitrary config and repo options"))
Expand Down
42 changes: 42 additions & 0 deletions dnf/conf/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,53 @@ def releasever(self):
@releasever.setter
def releasever(self, val):
# :api
"""
Sets the releasever variable and sets releasever_major and
releasever_minor accordingly. releasever_major is set to the part of
$releasever before the first ".". releasever_minor is set to the part
after the first ".".
"""
if val is None:
self.substitutions.pop('releasever', None)
return
self.substitutions['releasever'] = str(val)

@property
def releasever_major(self):
# :api
return self.substitutions.get('releasever_major')

@releasever_major.setter
def releasever_major(self, val):
# :api
"""
Override the releasever_major variable, which is usually derived from
the releasever variable. This setter does not update the value of
$releasever.
"""
if val is None:
self.substitutions.pop('releasever_major', None)
return
self.substitutions['releasever_major'] = str(val)

@property
def releasever_minor(self):
# :api
"""
Override the releasever_minor variable, which is usually derived from
the releasever variable. This setter does not update the value of
$releasever.
"""
return self.substitutions.get('releasever_minor')

@releasever_minor.setter
def releasever_minor(self, val):
# :api
if val is None:
self.substitutions.pop('releasever_minor', None)
return
self.substitutions['releasever_minor'] = str(val)

@property
def arch(self):
# :api
Expand Down
17 changes: 3 additions & 14 deletions dnf/conf/substitutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
import os
import re

from libdnf.conf import ConfigParser
from dnf.i18n import _
from dnf.exceptions import ReadOnlyVariableError

ENVIRONMENT_VARS_RE = re.compile(r'^DNF_VAR_[A-Za-z0-9_]+$')
READ_ONLY_VARIABLES = frozenset(("releasever_major", "releasever_minor"))
READ_ONLY_VARIABLES = frozenset()
logger = logging.getLogger('dnf')


Expand All @@ -45,18 +46,6 @@ def _update_from_env(self):
elif key in numericvars:
self[key] = val

@staticmethod
def _split_releasever(releasever):
# type: (str) -> tuple[str, str]
pos = releasever.find(".")
if pos == -1:
releasever_major = releasever
releasever_minor = ""
else:
releasever_major = releasever[:pos]
releasever_minor = releasever[pos + 1:]
return releasever_major, releasever_minor

def __setitem__(self, key, value):
if Substitutions.is_read_only(key):
raise ReadOnlyVariableError(f"Variable \"{key}\" is read-only", variable_name=key)
Expand All @@ -65,7 +54,7 @@ def __setitem__(self, key, value):
setitem(key, value)

if key == "releasever" and value:
releasever_major, releasever_minor = Substitutions._split_releasever(value)
releasever_major, releasever_minor = ConfigParser.splitReleasever(value)
setitem("releasever_major", releasever_major)
setitem("releasever_minor", releasever_minor)

Expand Down
2 changes: 2 additions & 0 deletions dnf/const.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ CONF_AUTOMATIC_FILENAME='/etc/dnf/automatic.conf'
DISTROVERPKG=('system-release(releasever)', 'system-release',
'distribution-release(releasever)', 'distribution-release',
'redhat-release', 'suse-release')
DISTROVER_MAJOR_PKG='system-release(releasever_major)'
DISTROVER_MINOR_PKG='system-release(releasever_minor)'
GROUP_PACKAGE_TYPES = ('mandatory', 'default', 'conditional') # :api
INSTALLONLYPKGS=['kernel', 'kernel-PAE',
'installonlypkg(kernel)',
Expand Down
43 changes: 39 additions & 4 deletions dnf/rpm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@
import rpm # used by ansible (dnf.rpm.rpm.labelCompare in lib/ansible/modules/packaging/os/dnf.py)


def detect_releasever(installroot):
def detect_releasevers(installroot):
# :api
"""Calculate the release version for the system."""
"""Calculate the release version for the system, including releasever_major
and releasever_minor if they are overriden by the system-release-major or
system-release-minor provides."""

ts = transaction.initReadOnlyTransaction(root=installroot)
ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES | rpm._RPMVSF_NODIGESTS))

distrover_major_pkg = dnf.const.DISTROVER_MAJOR_PKG
distrover_minor_pkg = dnf.const.DISTROVER_MINOR_PKG
if dnf.pycomp.PY3:
distrover_major_pkg = bytes(distrover_major_pkg, 'utf-8')
distrover_minor_pkg = bytes(distrover_minor_pkg, 'utf-8')

for distroverpkg in dnf.const.DISTROVERPKG:
if dnf.pycomp.PY3:
distroverpkg = bytes(distroverpkg, 'utf-8')
Expand All @@ -47,6 +56,8 @@ def detect_releasever(installroot):
msg = 'Error: rpmdb failed to list provides. Try: rpm --rebuilddb'
raise dnf.exceptions.Error(msg)
releasever = hdr['version']
releasever_major = None
releasever_minor = None
try:
try:
# header returns bytes -> look for bytes
Expand All @@ -61,13 +72,37 @@ def detect_releasever(installroot):
if hdr['name'] not in (distroverpkg, distroverpkg.decode("utf8")):
# override the package version
releasever = ver

for provide, flag, ver in zip(
hdr[rpm.RPMTAG_PROVIDENAME],
hdr[rpm.RPMTAG_PROVIDEFLAGS],
hdr[rpm.RPMTAG_PROVIDEVERSION]):
if isinstance(provide, str):
provide = bytes(provide, "utf-8")
if provide == distrover_major_pkg and flag == rpm.RPMSENSE_EQUAL and ver:
releasever_major = ver
if provide == distrover_minor_pkg and flag == rpm.RPMSENSE_EQUAL and ver:
releasever_minor = ver

except (ValueError, KeyError, IndexError):
pass

if is_py3bytes(releasever):
releasever = str(releasever, "utf-8")
return releasever
return None
if is_py3bytes(releasever_major):
releasever_major = str(releasever_major, "utf-8")
if is_py3bytes(releasever_minor):
releasever_minor = str(releasever_minor, "utf-8")
return releasever, releasever_major, releasever_minor
return (None, None, None)


def detect_releasever(installroot):
# :api
"""Calculate the release version for the system."""

releasever, _, _ = detect_releasevers(installroot)
return releasever


def _header(path):
Expand Down
8 changes: 8 additions & 0 deletions doc/api_rpm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@

Returns ``None`` if the information can not be determined (perhaps because the tree has no RPMDB).

.. function:: detect_releasevers(installroot)

Returns a tuple of the release name, overridden major release, and overridden minor release of the distribution of the tree rooted at `installroot`. The function uses information from RPMDB found under the tree. The major and minor release versions are usually derived from the release version by splitting it on the first ``.``, but distributions can override the derived major and minor versions. It's preferred to use ``detect_releasevers`` over ``detect_releasever``; if you use the latter, you will not be aware of distribution overrides for the major and minor release versions.

Returns ``(None, None, None)`` if the information can not be determined (perhaps because the tree has no RPMDB).

If the distribution does not override the release major version, then the second item of the returned tuple will be ``None``. Likewise, if the release minor version is not overridden, the third return value will be ``None``.

.. function:: basearch(arch)

Return base architecture of the processor based on `arch` type given. E.g. when `arch` i686 is given then the returned value will be i386.
10 changes: 10 additions & 0 deletions doc/command_ref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,16 @@ Options
Configure DNF as if the distribution release was ``<release>``. This can
affect cache paths, values in configuration files and mirrorlist URLs.

``--releasever_major=<major version>``
Override the releasever_major variable, which is usually automatically
detected or taken from the part of ``$releasever`` before the first ``.``.
This option does not affect the ``$releasever`` variable.

``--releasever_minor=<minor version>``
Override the releasever_minor variable, which is usually automatically
detected or taken from the part of ``$releasever`` after the first ``.``.
This option does not affect the ``$releasever`` variable.

.. _repofrompath_options-label:


Expand Down
2 changes: 2 additions & 0 deletions doc/conf_ref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ configuration file by your distribution to override the DNF defaults.

The ``$releasever_major`` and ``$releasever_minor`` variables will be automatically derived from ``$releasever`` by splitting it on the first ``.``. For example, if ``$releasever`` is set to ``1.23``, then ``$releasever_major`` will be ``1`` and ``$releasever_minor`` will be ``23``.

``$releasever_major`` and ``$releasever_minor`` can also be set by the distribution.

See also :ref:`repo variables <repo-variables-label>`.

.. _reposdir-label:
Expand Down
6 changes: 5 additions & 1 deletion doc/examples/install_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@

with dnf.Base() as base:
# Substitutions are needed for correct interpretation of repo files.
RELEASEVER = dnf.rpm.detect_releasever(base.conf.installroot)
RELEASEVER, MAJOR, MINOR = dnf.rpm.detect_releasever(base.conf.installroot)
base.conf.substitutions['releasever'] = RELEASEVER
if MAJOR is not None:
base.conf.substitutions['releasever_major'] = MAJOR
if MINOR is not None:
base.conf.substitutions['releasever_minor'] = MINOR
# Repositories are needed if we want to install anything.
base.read_all_repos()
# A sack is required by marking methods and dependency resolving.
Expand Down
4 changes: 4 additions & 0 deletions tests/api/test_dnf_rpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def test_detect_releasever(self):
# dnf.rpm.detect_releasever
self.assertHasAttr(dnf.rpm, "detect_releasever")

def test_detect_releasevers(self):
# dnf.rpm.detect_releasevers
self.assertHasAttr(dnf.rpm, "detect_releasevers")

def test_basearch(self):
# dnf.rpm.basearch
self.assertHasAttr(dnf.rpm, "basearch")
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/commands/test_clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
'''
def _run(cli, args):
with mock.patch('sys.stdout', new_callable=StringIO), \
mock.patch('dnf.rpm.detect_releasever', return_value=69):
mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
cli.configure(['clean', '--config', '/dev/null'] + args)
cli.run()

Expand Down
19 changes: 9 additions & 10 deletions tests/conf/test_substitutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,6 @@ def test_named(self):
self.assertEqual('opera', conf.substitutions['GENRE'])


class SubstitutionsReadOnlyTest(tests.support.TestCase):
def test_set_readonly(self):
conf = dnf.conf.Conf()
variable_name = "releasever_major"
self.assertTrue(Substitutions.is_read_only(variable_name))
with self.assertRaises(ReadOnlyVariableError) as cm:
conf.substitutions["releasever_major"] = "1"
self.assertEqual(cm.exception.variable_name, "releasever_major")


class SubstitutionsReleaseverTest(tests.support.TestCase):
def test_releasever_simple(self):
conf = dnf.conf.Conf()
Expand All @@ -84,3 +74,12 @@ def test_releasever_multipart(self):
conf.substitutions["releasever"] = "1.23.45"
self.assertEqual(conf.substitutions["releasever_major"], "1")
self.assertEqual(conf.substitutions["releasever_minor"], "23.45")

def test_releasever_major_minor_overrides(self):
conf = dnf.conf.Conf()
conf.substitutions["releasever"] = "1.23"
conf.substitutions["releasever_major"] = "45"
conf.substitutions["releasever_minor"] = "67"
self.assertEqual(conf.substitutions["releasever"], "1.23")
self.assertEqual(conf.substitutions["releasever_major"], "45")
self.assertEqual(conf.substitutions["releasever_minor"], "67")
2 changes: 1 addition & 1 deletion tests/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def command_run(cmd, args):

class Base(dnf.Base):
def __init__(self, *args, **kwargs):
with mock.patch('dnf.rpm.detect_releasever', return_value=69):
with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
super(Base, self).__init__(*args, **kwargs)

# mock objects
Expand Down
4 changes: 2 additions & 2 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_instance(self):
self.assertIsNotNone(base)
base.close()

@mock.patch('dnf.rpm.detect_releasever', lambda x: 'x')
@mock.patch('dnf.rpm.detect_releasevers', lambda x: ('x', None, None))
@mock.patch('dnf.util.am_i_root', lambda: True)
def test_default_config_root(self):
base = dnf.Base()
Expand All @@ -67,7 +67,7 @@ def test_default_config_root(self):
self.assertIsNotNone(reg.match(base.conf.cachedir))
base.close()

@mock.patch('dnf.rpm.detect_releasever', lambda x: 'x')
@mock.patch('dnf.rpm.detect_releasevers', lambda x: ('x', None, None))
@mock.patch('dnf.util.am_i_root', lambda: False)
def test_default_config_user(self):
base = dnf.Base()
Expand Down
Loading
Loading