import sys import logging from mock import ( patch, call, Mock ) from pytest import ( raises, fixture ) from ..test_helper import argv_kiwi_tests from kiwi.system.root_bind import RootBind from kiwi.exceptions import ( KiwiMountKernelFileSystemsError, KiwiMountSharedDirectoryError, KiwiSetupIntermediateConfigError ) class TestRootBind: @fixture(autouse=True) def inject_fixtures(self, caplog): self._caplog = caplog def setup(self): root = Mock() root.root_dir = 'root-dir' self.bind_root = RootBind(root) # stub config files and bind locations self.bind_root.config_files = ['/etc/sysconfig/proxy'] self.bind_root.bind_locations = ['/proc'] # stub files/dirs and mountpoints to cleanup self.mount_manager = Mock() self.bind_root.cleanup_files = ['/etc/sysconfig/proxy.kiwi'] self.bind_root.mount_stack = [self.mount_manager] self.bind_root.dir_stack = ['/mountpoint'] def teardown(self): sys.argv = argv_kiwi_tests @patch('kiwi.system.root_bind.MountManager.bind_mount') @patch('kiwi.system.root_bind.RootBind.cleanup') @patch('os.path.exists') def test_kernel_file_systems_raises_error( self, mock_exists, mock_cleanup, mock_mount ): mock_exists.return_value = True mock_mount.side_effect = KiwiMountKernelFileSystemsError( 'mount-error' ) with raises(KiwiMountKernelFileSystemsError): self.bind_root.mount_kernel_file_systems() mock_cleanup.assert_called_once_with() @patch('kiwi.system.root_bind.MountManager.bind_mount') @patch('kiwi.system.root_bind.Path.create') @patch('kiwi.system.root_bind.RootBind.cleanup') def test_shared_directory_raises_error( self, mock_cleanup, mock_path, mock_mount ): mock_mount.side_effect = KiwiMountSharedDirectoryError( 'mount-error' ) with raises(KiwiMountSharedDirectoryError): self.bind_root.mount_shared_directory() mock_cleanup.assert_called_once_with() @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.RootBind.cleanup') @patch('os.path.exists') def test_intermediate_config_raises_error( self, mock_exists, mock_cleanup, mock_command ): mock_exists.return_value = True mock_command.side_effect = KiwiSetupIntermediateConfigError( 'config-error' ) with raises(KiwiSetupIntermediateConfigError): self.bind_root.setup_intermediate_config() mock_cleanup.assert_called_once_with() @patch('kiwi.system.root_bind.os.path.exists') @patch('kiwi.system.root_bind.MountManager') def test_mount_kernel_file_systems(self, mock_mount, mock_exists): mock_exists.return_value = True shared_mount = Mock() mock_mount.return_value = shared_mount self.bind_root.mount_kernel_file_systems() mock_mount.assert_called_once_with( device='/proc', mountpoint='root-dir/proc' ) shared_mount.bind_mount.assert_called_once_with() @patch('kiwi.system.root_bind.MountManager') @patch('kiwi.system.root_bind.Path.create') def test_mount_shared_directory(self, mock_path, mock_mount): shared_mount = Mock() mock_mount.return_value = shared_mount self.bind_root.mount_shared_directory() mock_path.call_args_list = [ call('root-dir/var/cache/kiwi'), call('/var/cache/kiwi') ] mock_mount.assert_called_once_with( device='/var/cache/kiwi', mountpoint='root-dir/var/cache/kiwi' ) shared_mount.bind_mount.assert_called_once_with() @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Checksum') @patch('os.path.exists') def test_intermediate_config( self, mock_exists, mock_Checksum, mock_command ): checksum = Mock() mock_Checksum.return_value = checksum mock_exists.return_value = True with patch('builtins.open') as m_open: self.bind_root.setup_intermediate_config() m_open.assert_called_once_with( 'root-dir/etc/sysconfig/proxy.sha', 'w' ) assert mock_command.call_args_list == [ call([ 'cp', '/etc/sysconfig/proxy', 'root-dir/etc/sysconfig/proxy.kiwi' ]), call([ 'ln', '-s', '-f', 'proxy.kiwi', 'root-dir/etc/sysconfig/proxy' ]) ] checksum.sha256.assert_called_once_with() @patch('kiwi.system.root_bind.Checksum') @patch('kiwi.system.root_bind.MountManager.is_mounted') @patch('kiwi.system.root_bind.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') @patch('os.path.islink') @patch('os.path.exists') @patch('shutil.move') def test_cleanup( self, mock_move, mock_exists, mock_islink, mock_remove_hierarchy, mock_command, mock_is_mounted, mock_Checksum ): checksum = Mock() checksum.matches.return_value = False mock_Checksum.return_value = checksum os_exists_return_values = [False, True, True, False] def exists_side_effect(*args): return os_exists_return_values.pop() mock_is_mounted.return_value = False mock_exists.side_effect = exists_side_effect mock_islink.return_value = True with self._caplog.at_level(logging.WARNING): self.bind_root.cleanup() self.mount_manager.umount_lazy.assert_called_once_with() mock_remove_hierarchy.assert_called_once_with('root-dir/mountpoint') assert mock_command.call_args_list == [ call(['rm', '-f', 'root-dir/etc/sysconfig/proxy']), call( [ 'cp', 'root-dir/usr/share/fillup-templates/sysconfig.proxy', 'root-dir/etc/sysconfig/proxy' ] ), call( [ 'rm', '-f', 'root-dir/etc/sysconfig/proxy.kiwi', 'root-dir/etc/sysconfig/proxy.sha' ] ) ] mock_move.assert_called_once_with( 'root-dir/etc/sysconfig/proxy.rpmnew', 'root-dir/etc/sysconfig/proxy' ) @patch('os.path.islink') @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') @patch('kiwi.system.root_bind.Checksum') def test_cleanup_continue_on_error( self, mock_Checksum, mock_remove_hierarchy, mock_command, mock_islink ): mock_islink.return_value = True mock_remove_hierarchy.side_effect = Exception('rm') mock_command.side_effect = Exception self.mount_manager.umount_lazy.side_effect = Exception with self._caplog.at_level(logging.WARNING): self.bind_root.cleanup() assert 'Image root directory root-dir not cleanly umounted:' in \ self._caplog.text assert 'Failed to remove directory hierarchy ' 'root-dir/mountpoint: rm' in self._caplog.text assert 'Failed to cleanup intermediate config files' in \ self._caplog.text @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') @patch('kiwi.system.root_bind.Checksum') def test_cleanup_nothing_mounted( self, mock_Checksum, mock_remove_hierarchy, mock_command ): self.mount_manager.is_mounted.return_value = False self.mount_manager.mountpoint = '/mountpoint' with self._caplog.at_level(logging.WARNING): self.bind_root.cleanup() assert 'Path /mountpoint not a mountpoint' in self._caplog.text