kiwi-el8/test/unit/mount_manager_test.py
Marcus Schäfer c27308377c
Raise exception on umount issue
Up to now kiwi tried to umount a resource it has mounted for some
time and if it was not possible due to a busy state the process
continued. In any of these situations it was not possible to free
up the nested resources and it could also happen that the image
build result got corrupted because there is still data that can
be consumed but shouldn't be there anymore. The change here makes
kiwi to stop the process and raise with an error also including
the information that there are now resources still active but
busy on the host and a manual cleanup is unfortunately needed.
This Fixes #1914
2021-10-20 22:57:44 +02:00

147 lines
5.4 KiB
Python

import logging
from pytest import (
fixture, raises
)
from mock import (
patch, call, Mock
)
from kiwi.exceptions import KiwiUmountBusyError
from kiwi.mount_manager import MountManager
class TestMountManager:
@fixture(autouse=True)
def inject_fixtures(self, caplog):
self._caplog = caplog
def setup(self):
self.mount_manager = MountManager(
'/dev/some-device', '/some/mountpoint'
)
@patch('kiwi.mount_manager.Temporary')
def test_setup_empty_mountpoint(self, mock_Temporary):
mock_Temporary.return_value.new_dir.return_value.name = 'tmpdir'
mount_manager = MountManager('/dev/some-device')
assert mount_manager.mountpoint == 'tmpdir'
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
def test_bind_mount(self, mock_mounted, mock_command):
mock_mounted.return_value = False
self.mount_manager.bind_mount()
mock_command.assert_called_once_with(
['mount', '-n', '--bind', '/dev/some-device', '/some/mountpoint']
)
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
def test_mount(self, mock_mounted, mock_command):
mock_mounted.return_value = False
self.mount_manager.mount(['options'])
mock_command.assert_called_once_with(
['mount', '-o', 'options', '/dev/some-device', '/some/mountpoint']
)
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
def test_umount_lazy(self, mock_mounted, mock_command):
mock_mounted.return_value = True
self.mount_manager.umount_lazy()
mock_command.assert_called_once_with(
['umount', '-l', '/some/mountpoint']
)
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
@patch('time.sleep')
def test_umount_with_errors(
self, mock_sleep, mock_mounted, mock_command
):
mock_command.side_effect = Exception
mock_mounted.return_value = True
with self._caplog.at_level(logging.WARNING):
assert self.mount_manager.umount(raise_on_busy=False) is False
assert mock_command.call_args_list == [
call(['umount', '/some/mountpoint']), # 1
call(['umount', '/some/mountpoint']), # 2
call(['umount', '/some/mountpoint']), # 3
call(['umount', '/some/mountpoint']), # 4
call(['umount', '/some/mountpoint']), # 5
call(['umount', '/some/mountpoint']), # 6
call(['umount', '/some/mountpoint']), # 7
call(['umount', '/some/mountpoint']), # 8
call(['umount', '/some/mountpoint']), # 9
call(['umount', '/some/mountpoint']) # 10
]
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
@patch('time.sleep')
@patch('kiwi.mount_manager.Path.which')
def test_umount_with_errors_raises_no_lsof_present(
self, mock_Path_which, mock_sleep, mock_mounted, mock_command
):
def command_call(args):
if 'umount' in args:
raise Exception
mock_Path_which.return_value = None
mock_command.side_effect = command_call
mock_mounted.return_value = True
with raises(KiwiUmountBusyError):
self.mount_manager.umount()
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
@patch('time.sleep')
@patch('kiwi.mount_manager.Path.which')
def test_umount_with_errors_raises_lsof_present(
self, mock_Path_which, mock_sleep, mock_mounted, mock_command
):
def command_call(args, raise_on_error=None):
if 'umount' in args:
raise Exception
else:
call_return = Mock()
call_return.output = 'HEADLINE\ndata'
return call_return
mock_Path_which.return_value = 'lsof'
mock_command.side_effect = command_call
mock_mounted.return_value = True
with raises(KiwiUmountBusyError) as issue:
self.mount_manager.umount()
assert 'HEADLINE' in issue.value.message
@patch('kiwi.mount_manager.Command.run')
@patch('kiwi.mount_manager.MountManager.is_mounted')
def test_umount_success(self, mock_mounted, mock_command):
mock_mounted.return_value = True
assert self.mount_manager.umount() is True
mock_command.assert_called_once_with(
['umount', '/some/mountpoint']
)
@patch('kiwi.mount_manager.Command.run')
def test_is_mounted_true(self, mock_command):
command = Mock()
command.returncode = 0
mock_command.return_value = command
assert self.mount_manager.is_mounted() is True
mock_command.assert_called_once_with(
command=['mountpoint', '-q', '/some/mountpoint'],
raise_on_error=False
)
@patch('kiwi.mount_manager.Command.run')
def test_is_mounted_false(self, mock_command):
command = Mock()
command.returncode = 1
mock_command.return_value = command
assert self.mount_manager.is_mounted() is False
mock_command.assert_called_once_with(
command=['mountpoint', '-q', '/some/mountpoint'],
raise_on_error=False
)