Skip to content
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
12 changes: 6 additions & 6 deletions build-tests/x86/centos/test-image-live-disk-v10/appliance.kiwi
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
<!-- The line below is required in order to use the multibuild OBS features -->
<!-- OBS-Profiles: @BUILD_FLAVOR@ -->

<image schemaversion="7.5" name="kiwi-test-image-live-disk-v9">
<image schemaversion="7.5" name="kiwi-test-image-live-disk-v10">
<description type="system">
<author>Marcus Schaefer</author>
<contact>marcus.schaefer@gmail.com</contact>
<specification>CentOS Stream 9 Appliance</specification>
<specification>CentOS Stream 10 Appliance</specification>
</description>
<profiles>
<profile name="Live" description="Live image of CentOS 9"/>
<profile name="Virtual" description="Virtual image of CentOS 9"/>
<profile name="Disk" description="OEM image of CentOS 9"/>
<profile name="Live" description="Live image of CentOS 10"/>
<profile name="Virtual" description="Virtual image of CentOS 10"/>
<profile name="Disk" description="OEM image of CentOS 10"/>
</profiles>
<preferences>
<version>1.3.0</version>
<packagemanager>dnf</packagemanager>
<packagemanager>dnf4</packagemanager>
<bootsplash-theme>charge</bootsplash-theme>
<locale>en_US</locale>
<keytable>us</keytable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</profiles>
<preferences>
<version>1.3.0</version>
<packagemanager>dnf</packagemanager>
<packagemanager>dnf4</packagemanager>
<bootsplash-theme>charge</bootsplash-theme>
<locale>en_US</locale>
<keytable>us</keytable>
Expand Down
42 changes: 36 additions & 6 deletions kiwi/package_manager/dnf4.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
import re
from typing import (
List, Dict
Expand Down Expand Up @@ -88,6 +89,31 @@ def request_package_exclusion(self, name: str) -> None:
"""
self.exclude_requests.append(name)

def _get_dnf4_binary_name(self, root=None):
"""
Identify whether dnf is 'dnf4' or 'dnf-3'

:param str root: lookup binary name below this root directory

:return: name of dnf4 command

:rtype: str
"""
dnf4_binary = 'dnf-3'
dnf4_search_env = {
'PATH': os.sep.join([root, 'usr', 'bin'])
} if root else None

# Python interpreter specific path
if Path.which(
filename='dnf4',
custom_env=dnf4_search_env,
access_mode=os.X_OK
):
dnf4_binary = 'dnf4'
Comment on lines +102 to +113
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can change this to

dnf_binary = 'dnf-4' if Path.which(
    filename='dnf4', access_mode=os.X_OK, root_dir=root
) else 'dnf-3'

return dnf_binary

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually means that we are using dnf-4 if that exists in the root tree of the image and else we are using dnf-3 unconditionally. Doesn't this open some room for issues ? What if dnf-3 doesn't exist ? Also when checking inside the root tree then checking outside of it would also be needed as the bootstrap phase will use the package manager from the host. The decision logic here is not quite clear to me

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the idea was to lookup PATH (host) and in $root then the code needs to run two lookup calls, because the first call would be happy if there is just one hit in the given PATH

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the exact implementation we used in the old yum one: https://github.com/OSInside/kiwi/blob/v9.16.36/kiwi/package_manager/yum.py#L89-L110

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least with all the current supported distributions, the dnf-3 binary exists unless the per-Python version suffixed binaries don't exist, and those have dnf4 instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just can't check for dnf because it's an ambiguous binary now.


return dnf4_binary

def setup_repository_modules(
self, collection_modules: Dict[str, List[str]]
) -> None:
Expand All @@ -109,7 +135,7 @@ def setup_repository_modules(
}
"""
dnf_module_command = [
'dnf'
self._get_dnf4_binary_name()
] + self.dnf_args + [
'--installroot', self.root_dir,
f'--releasever={self.release_version}'
Expand Down Expand Up @@ -147,6 +173,7 @@ def process_install_requests_bootstrap(

:rtype: namedtuple
"""
dnf4 = self._get_dnf4_binary_name()
exclude_args = []
if self.exclude_requests:
# For DNF, excluding a package means removing it from
Expand All @@ -156,12 +183,12 @@ def process_install_requests_bootstrap(
for package in self.exclude_requests:
exclude_args.append('--exclude=' + package)
Command.run(
['dnf'] + self.dnf_args + [
[dnf4] + self.dnf_args + [
f'--releasever={self.release_version}'
] + ['makecache']
)
dnf_command = [
'dnf'
dnf4
] + self.dnf_args + [
'--installroot', self.root_dir,
f'--releasever={self.release_version}'
Expand All @@ -181,6 +208,7 @@ def process_install_requests(self) -> CommandCallT:

:rtype: namedtuple
"""
dnf4 = self._get_dnf4_binary_name(self.root_dir)
exclude_args = []
if self.exclude_requests:
# For DNF, excluding a package means removing it from
Expand All @@ -193,7 +221,7 @@ def process_install_requests(self) -> CommandCallT:
self.root_dir, self.dnf_args
)
dnf_command = [
'chroot', self.root_dir, 'dnf'
'chroot', self.root_dir, dnf4
] + chroot_dnf_args + [
f'--releasever={self.release_version}'
] + self.custom_args + exclude_args + [
Expand Down Expand Up @@ -240,9 +268,10 @@ def process_delete_requests(self, force: bool = False) -> CommandCallT:
self.command_env
)
else:
dnf4 = self._get_dnf4_binary_name(self.root_dir)
chroot_dnf_args = Path.move_to_root(self.root_dir, self.dnf_args)
dnf_command = [
'chroot', self.root_dir, 'dnf'
'chroot', self.root_dir, dnf4
] + chroot_dnf_args + [
f'--releasever={self.release_version}'
] + self.custom_args + [
Expand All @@ -261,10 +290,11 @@ def update(self) -> CommandCallT:

:rtype: namedtuple
"""
dnf4 = self._get_dnf4_binary_name(self.root_dir)
chroot_dnf_args = Path.move_to_root(self.root_dir, self.dnf_args)
return Command.call(
[
'chroot', self.root_dir, 'dnf'
'chroot', self.root_dir, dnf4
] + chroot_dnf_args + [
f'--releasever={self.release_version}'
] + self.custom_args + [
Expand Down
48 changes: 37 additions & 11 deletions test/unit/package_manager/dnf4_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,34 @@ def test_request_package_exclusion(self):
self.manager.request_package_exclusion('name')
assert self.manager.exclude_requests == ['name']

@patch('kiwi.path.Path.which')
def test_get_dnf4_binary_name(self, mock_which):
mock_which.return_value = '/usr/bin/dnf4'
assert self.manager._get_dnf4_binary_name() == 'dnf4'
mock_which.assert_called_once_with(
access_mode=1, custom_env=None,
filename='dnf4'
)
mock_which.return_value = None
mock_which.reset_mock()
assert self.manager._get_dnf4_binary_name(root='/some/root') == 'dnf-3'
mock_which.assert_called_once_with(
access_mode=1, custom_env={'PATH': '/some/root/usr/bin'},
filename='dnf4'
)

@patch('kiwi.path.Path.which')
@patch('kiwi.command.Command.run')
def test_setup_repository_modules(self, mock_run):
def test_setup_repository_modules(self, mock_run, mock_exists):
mock_exists.return_value = None
self.manager.setup_repository_modules(
{
'disable': ['mod_c'],
'enable': ['mod_a:stream', 'mod_b']
}
)
dnf_call_args = [
'dnf', '--config', '/root-dir/dnf.conf',
'dnf-3', '--config', '/root-dir/dnf.conf',
'-y', '--installroot', '/root-dir', '--releasever=0'
]
assert mock_run.call_args_list == [
Expand Down Expand Up @@ -81,37 +99,41 @@ def test_setup_repository_modules(self, mock_run):
)
]

@patch('kiwi.path.Path.which')
@patch('kiwi.command.Command.call')
@patch('kiwi.command.Command.run')
def test_process_install_requests_bootstrap(self, mock_run, mock_call):
def test_process_install_requests_bootstrap(self, mock_run, mock_call, mock_exists):
mock_exists.return_value = None
self.manager.request_package('vim')
self.manager.request_collection('collection')
self.manager.request_package_exclusion('skipme')
self.manager.process_install_requests_bootstrap()
mock_run.assert_called_once_with(
[
'dnf', '--config', '/root-dir/dnf.conf', '-y',
'dnf-3', '--config', '/root-dir/dnf.conf', '-y',
'--releasever=0', 'makecache'
]
)
mock_call.assert_called_once_with(
[
'dnf', '--config', '/root-dir/dnf.conf', '-y',
'dnf-3', '--config', '/root-dir/dnf.conf', '-y',
'--installroot', '/root-dir', '--releasever=0',
'--exclude=skipme',
'install', 'vim', '@collection'
], ['env']
)

@patch('kiwi.path.Path.which')
@patch('kiwi.command.Command.call')
def test_process_install_requests(self, mock_call):
def test_process_install_requests(self, mock_call, mock_exists):
mock_exists.return_value = None
self.manager.request_package('vim')
self.manager.request_collection('collection')
self.manager.request_package_exclusion('skipme')
self.manager.process_install_requests()
mock_call.assert_called_once_with(
[
'chroot', '/root-dir', 'dnf', '--config', '/dnf.conf', '-y',
'chroot', '/root-dir', 'dnf-3', '--config', '/dnf.conf', '-y',
'--releasever=0', '--exclude=skipme',
'install', 'vim', '@collection'
], ['env']
Expand All @@ -132,14 +154,16 @@ def test_process_delete_requests_force(self, mock_run, mock_call):
]
)

@patch('kiwi.path.Path.which')
@patch('kiwi.command.Command.call')
@patch('kiwi.command.Command.run')
def test_process_delete_requests_no_force(self, mock_run, mock_call):
def test_process_delete_requests_no_force(self, mock_run, mock_call, mock_exists):
mock_exists.return_value = None
self.manager.request_package('vim')
self.manager.process_delete_requests()
mock_call.assert_called_once_with(
[
'chroot', '/root-dir', 'dnf',
'chroot', '/root-dir', 'dnf-3',
'--config', '/dnf.conf', '-y',
'--releasever=0', 'autoremove', 'vim'
],
Expand All @@ -159,12 +183,14 @@ def test_process_delete_requests_package_missing(
['chroot', '/root-dir', 'rpm', '-q', 'vim']
)

@patch('kiwi.path.Path.which')
@patch('kiwi.command.Command.call')
def test_update(self, mock_call):
def test_update(self, mock_call, mock_exists):
mock_exists.return_value = None
self.manager.update()
mock_call.assert_called_once_with(
[
'chroot', '/root-dir', 'dnf',
'chroot', '/root-dir', 'dnf-3',
'--config', '/dnf.conf', '-y',
'--releasever=0', 'upgrade'
], ['env']
Expand Down