Ejemplo n.º 1
0
def PrepareKiwiRootfsSection(descdir, destdir, offset=None):
	# initialize state
	state = XMLState(XMLDescription(descdir + '/config.xml').load())

	# collct signing keys
	repokeys = glob(descdir + '/*.key')

	# install chroot
	system = SystemPrepare(xml_state=state, root_dir=destdir, allow_existing=True)
	pkgmanager = system.setup_repositories(clear_cache=False, signing_keys=repokeys)
	system.install_bootstrap(manager=pkgmanager, plus_packages=None)
	pkgmanager.root_dir = destdir
	system.install_system(manager=pkgmanager)

	# configure chroot
	setup = SystemSetup(xml_state=state, root_dir=destdir)
	setup.import_description()
	setup.setup_groups()
	setup.setup_users()
	setup.call_config_script()

	# clean
	del pkgmanager
	del setup
	del system

	return KiwiRootfsSection(path=destdir, state=state, offset=offset)
Ejemplo n.º 2
0
	def finalize(self, image):
		# mount fs
		mount = MountManager(device=self._device)
		mount.mount(options=None)

		# run post-image script
		setup = SystemSetup(xml_state=self._state, root_dir=mount.mountpoint)
		setup.import_description()
		setup.call_disk_script()

		# clean-up
		mount.umount()
		del setup
		return True
Ejemplo n.º 3
0
    def process(self):  # noqa: C901
        """
        Prepare and install a new system for chroot access
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        self.load_xml_description(self.command_args['--description'])

        abs_root_path = os.path.abspath(self.command_args['--root'])

        prepare_checks = self.checks_before_command_args
        prepare_checks.update(
            {'check_target_directory_not_in_shared_cache': [abs_root_path]})
        self.run_checks(prepare_checks)

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo']))

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(*self.sextuple_token(add_repo))

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag'])

        if self.command_args['--add-container-label']:
            for add_label in self.command_args['--add-container-label']:
                try:
                    (name, value) = add_label.split('=', 1)
                    self.xml_state.add_container_config_label(name, value)
                except Exception:
                    log.warning('Container label {0} ignored. Invalid format: '
                                'expected labelname=value'.format(add_label))

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from'])

        self.run_checks(self.checks_after_command_args)

        log.info('Preparing system')
        system = SystemPrepare(self.xml_state, abs_root_path,
                               self.command_args['--allow-existing-root'])
        manager = system.setup_repositories(self.command_args['--clear-cache'],
                                            self.command_args['--signing-key'])
        system.install_bootstrap(manager,
                                 self.command_args['--add-bootstrap-package'])
        system.install_system(manager)

        if self.command_args['--add-package']:
            system.install_packages(manager,
                                    self.command_args['--add-package'])
        if self.command_args['--delete-package']:
            system.delete_packages(manager,
                                   self.command_args['--delete-package'])

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(self.xml_state, abs_root_path)
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()
        setup.setup_permissions()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # delete any custom rpm macros created
        Rpm(abs_root_path,
            Defaults.get_custom_rpm_image_macro_name()).wipe_config()

        # make sure system instance is cleaned up now
        del system
Ejemplo n.º 4
0
class TestSystemSetup:
    @patch('platform.machine')
    def setup(self, mock_machine):
        mock_machine.return_value = 'x86_64'
        self.context_manager_mock = mock.Mock()
        self.file_mock = mock.Mock()
        self.enter_mock = mock.Mock()
        self.exit_mock = mock.Mock()
        self.enter_mock.return_value = self.file_mock
        setattr(self.context_manager_mock, '__enter__', self.enter_mock)
        setattr(self.context_manager_mock, '__exit__', self.exit_mock)
        self.xml_state = mock.MagicMock()
        self.xml_state.get_package_manager = mock.Mock(return_value='zypper')
        self.xml_state.build_type.get_filesystem = mock.Mock(
            return_value='ext3')
        self.xml_state.xml_data.get_name = mock.Mock(return_value='some-image')
        self.xml_state.get_image_version = mock.Mock(return_value='1.2.3')
        self.xml_state.xml_data.description_dir = 'description_dir'
        self.setup = SystemSetup(self.xml_state, 'root_dir')
        description = XMLDescription(description='../data/example_config.xml',
                                     derived_from='derived/description')
        self.setup_with_real_xml = SystemSetup(XMLState(description.load()),
                                               'root_dir')
        command_run = namedtuple('command', ['output', 'error', 'returncode'])
        self.run_result = command_run(output='password-hash\n',
                                      error='stderr',
                                      returncode=0)

    @patch('platform.machine')
    def test_setup_ix86(self, mock_machine):
        mock_machine.return_value = 'i686'
        setup = SystemSetup(mock.MagicMock(), 'root_dir')
        assert setup.arch == 'ix86'

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @patch('kiwi.system.setup.glob.iglob')
    def test_import_description(self, mock_iglob, mock_path, mock_open,
                                mock_command):
        mock_iglob.return_value = ['config-cdroot.tar.xz']
        mock_path.return_value = True
        self.setup_with_real_xml.import_description()

        mock_iglob.assert_called_once_with('../data/config-cdroot.tar*')
        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(['cp', '../data/bootstrap.tgz', 'root_dir/image/']),
            call(['cp', 'config-cdroot.tar.xz', 'root_dir/image/'])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description_archive_from_derived(self, mock_path,
                                                     mock_open, mock_command):
        path_return_values = [True, False, True, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(
                ['cp', 'derived/description/bootstrap.tgz', 'root_dir/image/'])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_editboot_scripts_not_found(
            self, mock_path, mock_open, mock_command):
        path_return_values = [False, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_archives_not_found(
            self, mock_path, mock_open, mock_command):
        path_return_values = [False, False, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    def test_cleanup(self, mock_command):
        self.setup.cleanup()
        mock_command.assert_called_once_with(
            ['rm', '-r', '-f', '/.kconfig', '/image'])

    @patch_open
    def test_import_shell_environment(self, mock_open):
        mock_profile = mock.MagicMock()
        mock_profile.create = mock.Mock(return_value=['a'])
        mock_open.return_value = self.context_manager_mock
        self.setup.import_shell_environment(mock_profile)
        mock_profile.create.assert_called_once_with()
        mock_open.assert_called_once_with('root_dir/.profile', 'w')
        self.file_mock.write.assert_called_once_with('a\n')

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('kiwi.system.setup.glob.iglob')
    def test_import_cdroot_files(self, mock_iglob, mock_ArchiveTar):
        archive = mock.Mock()
        mock_ArchiveTar.return_value = archive
        mock_iglob.return_value = ['config-cdroot.tar.xz']
        self.setup.import_cdroot_files('target_dir')
        mock_iglob.assert_called_once_with(
            'description_dir/config-cdroot.tar*')
        mock_ArchiveTar.assert_called_once_with('config-cdroot.tar.xz')
        archive.extract.assert_called_once_with('target_dir')

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_copy_links(self, mock_os_path, mock_DataSync,
                                             mock_command):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=True,
                                        preserve_owner_group=True)
        mock_DataSync.assert_called_once_with('description_dir/root/',
                                              'root_dir')
        data.sync_data.assert_called_once_with(options=[
            '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system',
            '--copy-links', '-o', '-g'
        ])

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_links(self, mock_os_path, mock_DataSync,
                                        mock_command):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=False,
                                        preserve_owner_group=True)
        mock_DataSync.assert_called_once_with('description_dir/root/',
                                              'root_dir')
        data.sync_data.assert_called_once_with(options=[
            '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system',
            '--links', '-o', '-g'
        ])

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('os.path.exists')
    def test_import_overlay_files_from_archive(self, mock_os_path,
                                               mock_archive):
        archive = mock.Mock()
        mock_archive.return_value = archive

        exists_results = [True, False]

        def side_effect(arg):
            return exists_results.pop()

        mock_os_path.side_effect = side_effect

        self.setup.import_overlay_files()

        mock_archive.assert_called_once_with('description_dir/root.tar.gz')
        archive.extract.assert_called_once_with('root_dir')

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"'])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map_with_systemd(self, mock_path, mock_run,
                                             mock_shell, mock_caps):
        mock_caps.return_value = True
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot', '--keymap=keytable'
            ])
        ])

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale(self, mock_path, mock_run, mock_shell, mock_caps):
        mock_caps.return_valure = True
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--locale=locale1.UTF-8'
            ])
        ])
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8'])

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale_POSIX(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'POSIX,locale2'
        self.setup.setup_locale()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/language', 'RC_LANG', 'POSIX'])

    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone(self, mock_command):
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call([
                'chroot', 'root_dir', 'ln', '-s', '-f',
                '/usr/share/zoneinfo/timezone', '/etc/localtime'
            ])
        ])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone_with_systemd(self, mock_command, mock_caps):
        mock_caps.return_value = True
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/localtime']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--timezone=timezone'
            ])
        ])

    @patch('kiwi.system.setup.Users')
    def test_setup_groups(self, mock_users):
        users = mock.Mock()
        users.group_exists = mock.Mock(return_value=False)
        mock_users.return_value = users

        self.setup_with_real_xml.setup_groups()

        calls = [call('users'), call('kiwi'), call('admin')]
        users.group_exists.assert_has_calls(calls)

        calls = [call('users', []), call('kiwi', []), call('admin', [])]
        users.group_add.assert_has_calls(calls)

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_add(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(return_value=False)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob', '-m', '-d', '/root'
            ]),
            call('tux', [
                '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux'
            ]),
            call('kiwi', [
                '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m',
                '-d', '/home/kiwi'
            ])
        ]
        users.user_add.assert_has_calls(calls)

        mock_command.assert_called_with(
            ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd'])

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(return_value=True)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()
        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob'
            ]),
            call('tux', ['-p', 'password-hash', '-g', 'users']),
            call('kiwi',
                 ['-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users'])
        ]
        users.user_modify.assert_has_calls(calls)

    @patch('kiwi.system.setup.Path.which')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_plymouth_splash(self, mock_command, mock_which):
        mock_which.return_value = 'plymouth-set-default-theme'
        preferences = mock.Mock()
        preferences.get_bootsplash_theme = mock.Mock(
            return_value=['some-theme'])
        self.xml_state.get_preferences_sections = mock.Mock(
            return_value=[preferences])
        self.setup.setup_plymouth_splash()
        mock_which.assert_called_once_with(
            custom_env={'PATH': 'root_dir/usr/sbin'},
            filename='plymouth-set-default-theme')
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme'])

    @patch_open
    @patch('os.path.exists')
    def test_import_image_identifier(self, mock_os_path, mock_open):
        self.xml_state.xml_data.get_id = mock.Mock(return_value='42')
        mock_os_path.return_value = True
        mock_open.return_value = self.context_manager_mock
        self.setup.import_image_identifier()
        mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w')
        self.file_mock.write.assert_called_once_with('42\n')

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    def test_call_non_excutable_config_script(self, mock_access, mock_stat,
                                              mock_os_path, mock_watch,
                                              mock_command):
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_config_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/config.sh'])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    def test_call_excutable_config_script(self, mock_access, mock_stat,
                                          mock_os_path, mock_watch,
                                          mock_command):
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result

        # pretend that the script is executable
        mock_access.return_value = True
        self.setup.call_config_script()

        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', '/image/config.sh'])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    def test_call_image_script(self, mock_access, mock_stat, mock_os_path,
                               mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_image_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/images.sh'])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_config_script(self, mock_abspath, mock_exists,
                                          mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_config_script('ext4', 1)
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_config.sh')
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh '
            'ext4 1'
        ])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_install_script(self, mock_abspath, mock_exists,
                                           mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script('my_image.raw',
                                                 '/dev/mapper/loop0p1')
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_install.sh')
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh '
            'my_image.raw /dev/mapper/loop0p1'
        ])

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    def test_call_image_script_raises(self, mock_access, mock_stat,
                                      mock_os_path, mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_image_script()

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script_raises(self, mock_os_path,
                                                  mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script('my_image.raw',
                                                 '/dev/mapper/loop0p1')

    @patch('kiwi.command.Command.run')
    def test_create_init_link_from_linuxrc(self, mock_command):
        self.setup.create_init_link_from_linuxrc()
        mock_command.assert_called_once_with(
            ['ln', 'root_dir/linuxrc', 'root_dir/init'])

    @patch('kiwi.command.Command.run')
    def test_create_recovery_archive_cleanup_only(self, mock_command):
        self.setup.oemconfig['recovery'] = False
        self.setup.create_recovery_archive()
        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])

    @patch_open
    @patch('os.path.exists')
    @patch('kiwi.system.setup.Path.wipe')
    @patch('kiwi.command.Command.run')
    def test_create_fstab(self, mock_command, mock_wipe, mock_exists,
                          mock_open):
        mock_exists.return_value = True
        mock_open.return_value = self.context_manager_mock
        self.file_mock.read.return_value = 'append_entry'
        self.setup.create_fstab(['fstab_entry'])

        assert mock_open.call_args_list == [
            call('root_dir/etc/fstab', 'w'),
            call('root_dir/etc/fstab.append', 'r')
        ]
        assert self.file_mock.write.call_args_list == [
            call('fstab_entry\n'), call('append_entry')
        ]
        assert mock_command.call_args_list == [
            call(['patch', 'root_dir/etc/fstab', 'root_dir/etc/fstab.patch']),
            call(['chroot', 'root_dir', '/etc/fstab.script'])
        ]
        assert mock_wipe.call_args_list == [
            call('root_dir/etc/fstab.append'),
            call('root_dir/etc/fstab.patch'),
            call('root_dir/etc/fstab.script')
        ]

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.NamedTemporaryFile')
    @patch('kiwi.system.setup.ArchiveTar')
    @patch_open
    @patch('kiwi.system.setup.Compress')
    @patch('os.path.getsize')
    @patch('kiwi.system.setup.Path.wipe')
    def test_create_recovery_archive(self, mock_wipe, mock_getsize,
                                     mock_compress, mock_open, mock_archive,
                                     mock_temp, mock_command):
        mock_open.return_value = self.context_manager_mock
        mock_getsize.return_value = 42
        compress = mock.Mock()
        mock_compress.return_value = compress
        archive = mock.Mock()
        mock_archive.return_value = archive
        tmpdir = mock.Mock()
        tmpdir.name = 'tmpdir'
        mock_temp.return_value = tmpdir
        self.setup.oemconfig['recovery'] = True
        self.setup.oemconfig['recovery_inplace'] = True

        self.setup.create_recovery_archive()

        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])
        mock_archive.assert_called_once_with(create_from_file_list=False,
                                             filename='tmpdir')
        archive.create.assert_called_once_with(exclude=['dev', 'proc', 'sys'],
                                               options=[
                                                   '--numeric-owner',
                                                   '--hard-dereference',
                                                   '--preserve-permissions'
                                               ],
                                               source_dir='root_dir')
        assert mock_command.call_args_list[1] == call(
            ['mv', 'tmpdir', 'root_dir/recovery.tar'])
        assert mock_open.call_args_list[0] == call(
            'root_dir/recovery.tar.filesystem', 'w')
        assert self.file_mock.write.call_args_list[0] == call('ext3')
        assert mock_command.call_args_list[2] == call(
            ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l'])
        assert mock_open.call_args_list[1] == call(
            'root_dir/recovery.tar.files', 'w')
        assert mock_getsize.call_args_list[0] == call('root_dir/recovery.tar')
        assert self.file_mock.write.call_args_list[1] == call('1\n')
        assert mock_open.call_args_list[2] == call(
            'root_dir/recovery.tar.size', 'w')
        assert self.file_mock.write.call_args_list[2] == call('42')
        mock_compress.assert_called_once_with('root_dir/recovery.tar')
        compress.gzip.assert_called_once_with()
        assert mock_getsize.call_args_list[1] == call(
            'root_dir/recovery.tar.gz')
        assert mock_open.call_args_list[3] == call(
            'root_dir/recovery.partition.size', 'w')
        assert self.file_mock.write.call_args_list[3] == call('300')
        mock_wipe.assert_called_once_with('root_dir/recovery.tar.gz')

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.create')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_export_modprobe_setup(self, mock_exists, mock_DataSync, mock_path,
                                   mock_command):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_exists.return_value = True
        self.setup.export_modprobe_setup('target_root_dir')
        mock_path.assert_called_once_with('target_root_dir/etc')
        mock_DataSync.assert_called_once_with('root_dir/etc/modprobe.d',
                                              'target_root_dir/etc/')
        data.sync_data.assert_called_once_with(options=['-a'])

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.RpmDataBase')
    @patch('kiwi.system.setup.MountManager')
    @patch_open
    def test_export_package_list_rpm(self, mock_open, mock_MountManager,
                                     mock_RpmDataBase, mock_command):
        rpmdb = mock.Mock()
        rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath'
        rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath'
        rpmdb.has_rpm.return_value = True
        mock_RpmDataBase.return_value = rpmdb
        command = mock.Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|'
            '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'image_dbpath'
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w')
        rpmdb.has_rpm.return_value = False
        mock_command.reset_mock()
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|'
            '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'host_dbpath'
        ])

    @patch('kiwi.system.setup.os.path.exists')
    @patch_open
    def test_setup_machine_id(self, mock_open, mock_path_exists):
        mock_path_exists.return_value = True
        self.setup.setup_machine_id()
        mock_path_exists.return_value = False
        self.setup.setup_machine_id()
        mock_open.assert_called_once_with('root_dir/etc/machine-id', 'w')

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.which')
    @patch('kiwi.logger.log.warning')
    def test_setup_permissions(self, mock_log_warn, mock_path_which,
                               mock_command):
        mock_path_which.return_value = 'chkstat'
        self.setup.setup_permissions()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'chkstat', '--system', '--set'])
        mock_path_which.return_value = None
        self.setup.setup_permissions()
        mock_log_warn.assert_called_once()

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_list_dpkg(self, mock_open, mock_command):
        command = mock.Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = mock.Mock(return_value='apt-get')
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W', '-f',
            '${Package}|None|${Version}|None|${Architecture}|None|None\\n'
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w')

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.RpmDataBase')
    @patch('kiwi.system.setup.MountManager')
    @patch_open
    def test_export_package_verification(self, mock_open, mock_MountManager,
                                         mock_RpmDataBase, mock_command):
        is_mounted_return = [True, False]

        def is_mounted():
            return is_mounted_return.pop()

        shared_mount = mock.Mock()
        shared_mount.is_mounted.side_effect = is_mounted
        mock_MountManager.return_value = shared_mount
        rpmdb = mock.Mock()
        rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath'
        rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath'
        rpmdb.has_rpm.return_value = True
        mock_RpmDataBase.return_value = rpmdb
        command = mock.Mock()
        command.output = 'verification_data'
        mock_command.return_value = command
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'image_dbpath'
        ],
                                             raise_on_error=False)
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w')
        mock_MountManager.assert_called_once_with(device='/dev',
                                                  mountpoint='root_dir/dev')
        shared_mount.bind_mount.assert_called_once_with()
        shared_mount.umount_lazy.assert_called_once_with()
        rpmdb.has_rpm.return_value = False
        is_mounted_return = [True, False]
        mock_command.reset_mock()
        shared_mount.reset_mock()
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'host_dbpath'
        ],
                                             raise_on_error=False)
        shared_mount.bind_mount.assert_called_once_with()
        shared_mount.umount_lazy.assert_called_once_with()

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_verification_dpkg(self, mock_open, mock_command):
        command = mock.Mock()
        command.output = 'verification_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = mock.Mock(return_value='apt-get')
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'dpkg', '--root', 'root_dir', '-V', '--verify-format', 'rpm'
        ],
                                             raise_on_error=False)
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w')

    @patch('kiwi.system.setup.Command.run')
    def test_set_selinux_file_contexts(self, mock_command):
        self.setup.set_selinux_file_contexts('security_context_file')
        mock_command.assert_called_once_with([
            'chroot', 'root_dir', 'setfiles', 'security_context_file', '/',
            '-v'
        ])

    @patch('kiwi.system.setup.Repository')
    @patch('kiwi.system.setup.Uri')
    def test_import_repositories_marked_as_imageinclude(
            self, mock_uri, mock_repo):
        uri = mock.Mock()
        mock_uri.return_value = uri
        uri.translate = mock.Mock(return_value="uri")
        uri.alias = mock.Mock(return_value="uri-alias")
        uri.credentials_file_name = mock.Mock(
            return_value='kiwiRepoCredentials')
        mock_uri.return_value = uri
        repo = mock.Mock()
        mock_repo.return_value = repo
        self.setup_with_real_xml.import_repositories_marked_as_imageinclude()
        assert repo.add_repo.call_args_list[0] == call('uri-alias', 'uri',
                                                       'rpm-md', None, None,
                                                       None, None, None,
                                                       'kiwiRepoCredentials',
                                                       None, None)
Ejemplo n.º 5
0
    def process(self):                                      # noqa: C901
        """
        Build a system image from the specified description. The
        build command combines the prepare and create commands
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        abs_target_dir_path = os.path.abspath(
            self.command_args['--target-dir']
        )
        build_dir = os.sep.join([abs_target_dir_path, 'build'])
        image_root = os.sep.join([build_dir, 'image-root'])
        Path.create(build_dir)

        if not self.global_args['--logfile']:
            log.set_logfile(
                os.sep.join([abs_target_dir_path, 'build', 'image-root.log'])
            )

        self.load_xml_description(
            self.command_args['--description']
        )

        build_checks = self.checks_before_command_args
        build_checks.update(
            {
                'check_target_directory_not_in_shared_cache':
                    [abs_target_dir_path]
            }
        )
        self.run_checks(build_checks)

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo'])
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(
                    *self.sextuple_token(add_repo)
                )

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--add-container-label']:
            for add_label in self.command_args['--add-container-label']:
                try:
                    (name, value) = add_label.split('=', 1)
                    self.xml_state.add_container_config_label(name, value)
                except Exception:
                    log.warning(
                        'Container label {0} ignored. Invalid format: '
                        'expected labelname=value'.format(add_label)
                    )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.run_checks(self.checks_after_command_args)

        log.info('Preparing new root system')
        system = SystemPrepare(
            self.xml_state,
            image_root,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(
            manager, self.command_args['--add-bootstrap-package']
        )
        system.install_system(
            manager
        )
        if self.command_args['--add-package']:
            system.install_packages(
                manager, self.command_args['--add-package']
            )
        if self.command_args['--delete-package']:
            system.delete_packages(
                manager, self.command_args['--delete-package']
            )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)
        profile.create(
            Defaults.get_profile_file(image_root)
        )

        setup = SystemSetup(
            self.xml_state, image_root
        )

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()
        setup.setup_permissions()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # delete any custom rpm macros created
        system.clean_package_manager_leftovers()

        # make sure system instance is cleaned up now
        del system

        setup.call_image_script()

        # make sure setup instance is cleaned up now
        del setup

        log.info('Creating system image')
        image_builder = ImageBuilder.new(
            self.xml_state,
            abs_target_dir_path,
            image_root,
            custom_args={
                'signing_keys': self.command_args['--signing-key'],
                'xz_options': self.runtime_config.get_xz_options()
            }
        )
        result = image_builder.create()
        result.print_results()
        result.dump(
            os.sep.join([abs_target_dir_path, 'kiwi.result'])
        )
Ejemplo n.º 6
0
class TestSystemSetup:
    @fixture(autouse=True)
    def inject_fixtures(self, caplog):
        self._caplog = caplog

    @patch('kiwi.system.setup.RuntimeConfig')
    def setup(self, mock_RuntimeConfig):
        Defaults.set_platform_name('x86_64')
        self.runtime_config = Mock()
        self.runtime_config.get_package_changes = Mock(return_value=True)
        mock_RuntimeConfig.return_value = self.runtime_config
        self.xml_state = MagicMock()
        self.xml_state.get_package_manager = Mock(return_value='zypper')
        self.xml_state.build_type.get_filesystem = Mock(return_value='ext3')
        self.xml_state.xml_data.get_name = Mock(return_value='some-image')
        self.xml_state.get_image_version = Mock(return_value='1.2.3')
        self.xml_state.xml_data.description_dir = 'description_dir'
        self.setup = SystemSetup(self.xml_state, 'root_dir')
        description = XMLDescription(description='../data/example_config.xml',
                                     derived_from='derived/description')
        self.description_dir = os.path.dirname(description.description_origin)
        self.setup_with_real_xml = SystemSetup(XMLState(description.load()),
                                               'root_dir')
        command_run = namedtuple('command', ['output', 'error', 'returncode'])
        self.run_result = command_run(output='password-hash\n',
                                      error='stderr',
                                      returncode=0)

    def teardown(self):
        sys.argv = argv_kiwi_tests

    def test_setup_ix86(self):
        Defaults.set_platform_name('i686')
        setup = SystemSetup(MagicMock(), 'root_dir')
        assert setup.arch == 'ix86'

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    @patch('kiwi.system.setup.glob.iglob')
    def test_import_description(self, mock_iglob, mock_path, mock_command):
        mock_iglob.return_value = ['config-cdroot.tar.xz']
        mock_path.return_value = True

        with patch('builtins.open'):
            self.setup_with_real_xml.import_description()

        mock_iglob.assert_called_once_with('{0}/config-cdroot.tar*'.format(
            self.description_dir))
        assert mock_command.call_args_list == [
            call([
                'cp', '{0}/config.sh'.format(self.description_dir),
                'root_dir/image/config.sh'
            ]),
            call([
                'cp', '{0}/disk.sh'.format(self.description_dir),
                'root_dir/image/disk.sh'
            ]),
            call([
                'cp', '{0}/my_edit_boot_script'.format(self.description_dir),
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call([
                'cp', '{0}/images.sh'.format(self.description_dir),
                'root_dir/image/images.sh'
            ]),
            call([
                'cp', '{0}/post_bootstrap.sh'.format(self.description_dir),
                'root_dir/image/post_bootstrap.sh'
            ]),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call([
                'cp', '{0}/bootstrap.tgz'.format(self.description_dir),
                'root_dir/image/'
            ]),
            call(['cp', 'config-cdroot.tar.xz', 'root_dir/image/'])
        ]

    @patch('kiwi.path.Path.create')
    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_description_archive_from_derived(self, mock_path,
                                                     mock_command,
                                                     mock_create):
        path_return_values = [
            True, False, True, True, True, True, True, True, True
        ]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect

        with patch('builtins.open'):
            self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call([
                'cp', '{0}/config.sh'.format(self.description_dir),
                'root_dir/image/config.sh'
            ]),
            call([
                'cp', '{0}/disk.sh'.format(self.description_dir),
                'root_dir/image/disk.sh'
            ]),
            call([
                'cp', '{0}/my_edit_boot_script'.format(self.description_dir),
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call([
                'cp', '{0}/images.sh'.format(self.description_dir),
                'root_dir/image/images.sh'
            ]),
            call([
                'cp', '{0}/post_bootstrap.sh'.format(self.description_dir),
                'root_dir/image/post_bootstrap.sh'
            ]),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(
                ['cp', 'derived/description/bootstrap.tgz', 'root_dir/image/'])
        ]
        mock_create.assert_called_once_with('root_dir/image')

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_description_configured_editboot_scripts_not_found(
            self, mock_path, mock_command):
        path_return_values = [False, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        with patch('builtins.open'):
            with raises(KiwiImportDescriptionError):
                self.setup_with_real_xml.import_description()

    @patch('kiwi.path.Path.create')
    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_description_configured_archives_not_found(
            self, mock_path, mock_command, mock_create):
        path_return_values = [False, False, True, True, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect

        with patch('builtins.open'):
            with raises(KiwiImportDescriptionError):
                self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    def test_cleanup(self, mock_command):
        self.setup.cleanup()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'rm', '-rf', '.kconfig', 'image'])

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('kiwi.system.setup.glob.iglob')
    def test_import_cdroot_files(self, mock_iglob, mock_ArchiveTar):
        archive = Mock()
        mock_ArchiveTar.return_value = archive
        mock_iglob.return_value = ['config-cdroot.tar.xz']
        self.setup.import_cdroot_files('target_dir')
        mock_iglob.assert_called_once_with(
            'description_dir/config-cdroot.tar*')
        mock_ArchiveTar.assert_called_once_with('config-cdroot.tar.xz')
        archive.extract.assert_called_once_with('target_dir')

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_copy_links(self, mock_os_path, mock_DataSync,
                                             mock_command):
        data = Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=True,
                                        preserve_owner_group=True)
        mock_DataSync.assert_called_once_with('description_dir/root/',
                                              'root_dir')
        data.sync_data.assert_called_once_with(options=[
            '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system',
            '--copy-links', '-o', '-g'
        ])

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_links(self, mock_os_path, mock_DataSync,
                                        mock_command):
        data = Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=False,
                                        preserve_owner_group=True)
        mock_DataSync.assert_called_once_with('description_dir/root/',
                                              'root_dir')
        data.sync_data.assert_called_once_with(options=[
            '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system',
            '--links', '-o', '-g'
        ])

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('os.path.exists')
    def test_import_overlay_files_from_archive(self, mock_os_path,
                                               mock_archive):
        archive = Mock()
        mock_archive.return_value = archive

        exists_results = [True, False]

        def side_effect(arg):
            return exists_results.pop()

        mock_os_path.side_effect = side_effect

        self.setup.import_overlay_files()

        mock_archive.assert_called_once_with('description_dir/root.tar.gz')
        archive.extract.assert_called_once_with('root_dir')

    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_from_profile(self, mock_os_path,
                                               mock_DataSync):
        exists_results = [True, False, False]

        def side_effect(arg):
            return exists_results.pop()

        data = Mock()
        mock_DataSync.return_value = data
        mock_os_path.side_effect = side_effect
        self.xml_state.profiles = ['profile_root']
        self.setup.import_overlay_files()
        mock_DataSync.assert_called_once_with('description_dir/profile_root/',
                                              'root_dir')
        data.sync_data.assert_called_once_with(options=[
            '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system',
            '--links'
        ])

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"'])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map_with_systemd(self, mock_path, mock_run,
                                             mock_shell, mock_caps):
        mock_caps.return_value = True
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot', '--keymap=keytable'
            ])
        ])

    @patch('os.path.exists')
    def test_setup_keyboard_skipped(self, mock_exists):
        mock_exists.return_value = False
        self.setup.preferences['keytable'] = 'keytable'
        with self._caplog.at_level(logging.WARNING):
            self.setup.setup_keyboard_map()

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale(self, mock_path, mock_run, mock_caps):
        mock_caps.return_valure = True
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--locale=locale1.UTF-8'
            ])
        ])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale_POSIX(self, mock_path, mock_run, mock_caps):
        mock_caps.return_valure = True
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'POSIX,locale2'
        self.setup.setup_locale()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']),
            call(['chroot', 'root_dir', 'systemd-firstboot', '--locale=POSIX'])
        ])

    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone(self, mock_command):
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call([
                'chroot', 'root_dir', 'ln', '-s', '-f',
                '/usr/share/zoneinfo/timezone', '/etc/localtime'
            ])
        ])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone_with_systemd(self, mock_command, mock_caps):
        mock_caps.return_value = True
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--timezone=timezone'
            ])
        ])

    @patch('kiwi.system.setup.Users')
    def test_setup_groups(self, mock_users):
        users = Mock()
        users.group_exists = Mock(return_value=False)
        mock_users.return_value = users

        self.setup_with_real_xml.setup_groups()

        calls = [call('users'), call('kiwi'), call('admin')]
        users.group_exists.assert_has_calls(calls)

        calls = [call('users', []), call('kiwi', []), call('admin', [])]
        users.group_add.assert_has_calls(calls)

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_add(self, mock_command, mock_users):
        users = Mock()
        users.user_exists = Mock(return_value=False)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob', '-m', '-d', '/root'
            ]),
            call('tux', [
                '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux'
            ]),
            call('kiwi', [
                '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m'
            ])
        ]
        users.user_add.assert_has_calls(calls)

        mock_command.assert_called_with(
            ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd'])

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify(self, mock_command, mock_users):
        users = Mock()
        users.user_exists = Mock(return_value=True)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()
        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob'
            ]),
            call('tux', ['-p', 'password-hash', '-g', 'users']),
            call('kiwi',
                 ['-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users'])
        ]
        users.user_modify.assert_has_calls(calls)

    @patch('kiwi.system.setup.Path.which')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_plymouth_splash(self, mock_command, mock_which):
        mock_which.return_value = 'plymouth-set-default-theme'
        preferences = Mock()
        preferences.get_bootsplash_theme = Mock(return_value=['some-theme'])
        self.xml_state.get_preferences_sections = Mock(
            return_value=[preferences])
        self.setup.setup_plymouth_splash()
        mock_which.assert_called_once_with(
            root_dir='root_dir', filename='plymouth-set-default-theme')
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme'])

    @patch('os.path.exists')
    def test_import_image_identifier(self, mock_os_path):
        self.xml_state.xml_data.get_id = Mock(return_value='42')
        mock_os_path.return_value = True

        m_open = mock_open()
        with patch('builtins.open', m_open, create=True):
            self.setup.import_image_identifier()

        m_open.assert_called_once_with('root_dir/etc/ImageID', 'w')
        m_open.return_value.write.assert_called_once_with('42\n')

    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    @patch('copy.deepcopy')
    def test_call_non_excutable_config_script(self, mock_copy_deepcopy,
                                              mock_access, mock_stat,
                                              mock_os_path, mock_watch,
                                              mock_command, mock_Profile):
        mock_copy_deepcopy.return_value = {}
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_config_script()
        mock_copy_deepcopy.assert_called_once_with(os.environ)
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', 'image/config.sh'], {})

    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    @patch('copy.deepcopy')
    def test_call_excutable_config_script(self, mock_copy_deepcopy,
                                          mock_access, mock_stat, mock_os_path,
                                          mock_watch, mock_command,
                                          mock_Profile):
        mock_copy_deepcopy.return_value = {}
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result

        # pretend that the script is executable
        mock_access.return_value = True
        self.setup.call_config_script()

        mock_copy_deepcopy.assert_called_once_with(os.environ)
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'image/config.sh'], {})

    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    @patch('copy.deepcopy')
    def test_call_excutable_post_bootstrap_script(self, mock_copy_deepcopy,
                                                  mock_access, mock_stat,
                                                  mock_os_path, mock_watch,
                                                  mock_command, mock_Profile):
        mock_copy_deepcopy.return_value = {}
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result

        # pretend that the script is executable
        mock_access.return_value = True
        self.setup.call_post_bootstrap_script()

        mock_copy_deepcopy.assert_called_once_with(os.environ)
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'image/post_bootstrap.sh'], {})

    @patch('kiwi.system.setup.Defaults.is_buildservice_worker')
    @patch('kiwi.logger.Logger.getLogLevel')
    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    @patch('copy.deepcopy')
    def test_call_disk_script(self, mock_copy_deepcopy, mock_access, mock_stat,
                              mock_os_path, mock_watch, mock_command,
                              mock_Profile, mock_getLogLevel,
                              mock_is_buildservice_worker):
        mock_is_buildservice_worker.return_value = False
        mock_getLogLevel.return_value = logging.DEBUG
        mock_copy_deepcopy.return_value = {}
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_disk_script()
        mock_copy_deepcopy.assert_called_once_with(os.environ)
        mock_command.assert_called_once_with([
            'screen', '-t', '-X', 'chroot', 'root_dir', 'bash', 'image/disk.sh'
        ], {})

    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    @patch('copy.deepcopy')
    def test_call_image_script(self, mock_copy_deepcopy, mock_access,
                               mock_stat, mock_os_path, mock_watch,
                               mock_command, mock_Profile):
        mock_copy_deepcopy.return_value = {}
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False

        self.setup.call_image_script()
        mock_copy_deepcopy.assert_called_once_with(os.environ)
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', 'image/images.sh'], {})

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_config_script(self, mock_abspath, mock_exists,
                                          mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_config_script('ext4', 1)
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_config.sh')
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh '
            'ext4 1'
        ])

    @patch('kiwi.system.setup.Defaults.is_buildservice_worker')
    @patch('kiwi.logger.Logger.getLogLevel')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_install_script(self, mock_abspath, mock_exists,
                                           mock_watch, mock_command,
                                           mock_getLogLevel,
                                           mock_is_buildservice_worker):
        mock_is_buildservice_worker.return_value = False
        mock_getLogLevel.return_value = logging.DEBUG
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script('my_image.raw',
                                                 '/dev/mapper/loop0p1')
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_install.sh')
        mock_command.assert_called_once_with([
            'screen', '-t', '-X', 'bash', '-c', 'cd root_dir && bash --norc '
            '/root_dir/image/edit_boot_install.sh '
            'my_image.raw /dev/mapper/loop0p1'
        ])

    @patch('kiwi.system.setup.Profile')
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.stat')
    @patch('os.access')
    def test_call_image_script_raises(self, mock_access, mock_stat,
                                      mock_os_path, mock_watch, mock_command,
                                      mock_Profile):
        profile = Mock()
        mock_Profile.return_value = profile
        profile.get_settings.return_value = {}
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        mock_access.return_value = False
        with raises(KiwiScriptFailed):
            self.setup.call_image_script()

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script_raises(self, mock_os_path,
                                                  mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        with raises(KiwiScriptFailed):
            self.setup.call_edit_boot_install_script('my_image.raw',
                                                     '/dev/mapper/loop0p1')

    @patch('kiwi.command.Command.run')
    def test_create_init_link_from_linuxrc(self, mock_command):
        self.setup.create_init_link_from_linuxrc()
        mock_command.assert_called_once_with(
            ['ln', 'root_dir/linuxrc', 'root_dir/init'])

    @patch('kiwi.command.Command.run')
    def test_create_recovery_archive_cleanup_only(self, mock_command):
        self.setup.oemconfig['recovery'] = False
        self.setup.create_recovery_archive()
        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])

    @patch('os.path.exists')
    @patch('kiwi.system.setup.Path.wipe')
    @patch('kiwi.command.Command.run')
    def test_create_fstab(self, mock_command, mock_wipe, mock_exists):
        fstab = Mock()
        mock_exists.return_value = True

        m_open = mock_open(read_data='append_entry')
        with patch('builtins.open', m_open, create=True):
            self.setup.create_fstab(fstab)

        fstab.export.assert_called_once_with('root_dir/etc/fstab')

        assert m_open.call_args_list == [
            call('root_dir/etc/fstab', 'a'),
            call('root_dir/etc/fstab.append', 'r')
        ]
        assert m_open.return_value.write.call_args_list == [
            call('append_entry')
        ]
        assert mock_command.call_args_list == [
            call(['patch', 'root_dir/etc/fstab', 'root_dir/etc/fstab.patch']),
            call(['chroot', 'root_dir', '/etc/fstab.script'])
        ]
        assert mock_wipe.call_args_list == [
            call('root_dir/etc/fstab.append'),
            call('root_dir/etc/fstab.patch'),
            call('root_dir/etc/fstab.script')
        ]

    @patch('kiwi.command.Command.run')
    @patch('pathlib.Path.touch')
    @patch('kiwi.system.setup.ArchiveTar')
    @patch('kiwi.system.setup.Compress')
    @patch('os.path.getsize')
    @patch('kiwi.system.setup.Path.wipe')
    def test_create_recovery_archive(self, mock_wipe, mock_getsize,
                                     mock_compress, mock_archive,
                                     mock_pathlib_Path_touch, mock_command):
        mock_getsize.return_value = 42
        compress = Mock()
        mock_compress.return_value = compress
        archive = Mock()
        mock_archive.return_value = archive
        self.setup.oemconfig['recovery'] = True
        self.setup.oemconfig['recovery_inplace'] = True

        m_open = mock_open()
        with patch('builtins.open', m_open, create=True):
            self.setup.create_recovery_archive()

        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])
        mock_archive.assert_called_once_with(create_from_file_list=False,
                                             filename='root_dir/recovery.tar')
        archive.create.assert_called_once_with(exclude=['dev', 'proc', 'sys'],
                                               options=[
                                                   '--numeric-owner',
                                                   '--hard-dereference',
                                                   '--preserve-permissions'
                                               ],
                                               source_dir='root_dir')
        assert m_open.call_args_list[0] == call(
            'root_dir/recovery.tar.filesystem', 'w')
        assert m_open.return_value.write.call_args_list[0] == call('ext3')

        assert mock_command.call_args_list[1] == call(
            ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l'])
        assert m_open.call_args_list[1] == call('root_dir/recovery.tar.files',
                                                'w')
        assert mock_getsize.call_args_list[0] == call('root_dir/recovery.tar')
        assert m_open.return_value.write.call_args_list[1] == call('1\n')
        assert m_open.call_args_list[2] == call('root_dir/recovery.tar.size',
                                                'w')
        assert m_open.return_value.write.call_args_list[2] == call('42')
        mock_compress.assert_called_once_with('root_dir/recovery.tar')
        compress.gzip.assert_called_once_with()
        assert mock_getsize.call_args_list[1] == call(
            'root_dir/recovery.tar.gz')
        assert m_open.call_args_list[3] == call(
            'root_dir/recovery.partition.size', 'w')
        assert m_open.return_value.write.call_args_list[3] == call('300')
        mock_wipe.assert_called_once_with('root_dir/recovery.tar.gz')

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.create')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_export_modprobe_setup(self, mock_exists, mock_DataSync, mock_path,
                                   mock_command):
        data = Mock()
        mock_DataSync.return_value = data
        mock_exists.return_value = True
        self.setup.export_modprobe_setup('target_root_dir')
        mock_path.assert_called_once_with('target_root_dir/etc')
        mock_DataSync.assert_called_once_with('root_dir/etc/modprobe.d',
                                              'target_root_dir/etc/')
        data.sync_data.assert_called_once_with(options=['-a'])

    @patch('kiwi.defaults.Defaults.get_default_packager_tool')
    def test_export_package_list_unknown_packager(
            self, mock_get_default_packager_tool):
        assert self.setup.export_package_list('target_dir') == ''

    @patch('kiwi.defaults.Defaults.get_default_packager_tool')
    def test_export_package_changes_unknown_packager(
            self, mock_get_default_packager_tool):
        assert self.setup.export_package_changes('target_dir') == ''

    @patch('kiwi.defaults.Defaults.get_default_packager_tool')
    def test_export_package_verification_unknown_packager(
            self, mock_get_default_packager_tool):
        assert self.setup.export_package_verification('target_dir') == ''

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.RpmDataBase')
    @patch('kiwi.system.setup.MountManager')
    def test_export_package_list_rpm(self, mock_MountManager, mock_RpmDataBase,
                                     mock_command):
        rpmdb = Mock()
        rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath'
        rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath'
        rpmdb.has_rpm.return_value = True
        mock_RpmDataBase.return_value = rpmdb
        command = Mock()
        command.output = 'packages_data'
        mock_command.return_value = command

        with patch('builtins.open') as m_open:
            result = self.setup.export_package_list('target_dir')
            m_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.packages',
                'w',
                encoding='utf-8')

        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|'
            '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'image_dbpath'
        ])
        rpmdb.has_rpm.return_value = False
        mock_command.reset_mock()

        with patch('builtins.open'):
            result = self.setup.export_package_list('target_dir')

        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|'
            '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'host_dbpath'
        ])

    @patch('kiwi.system.setup.os.path.exists')
    def test_setup_machine_id(self, mock_path_exists):
        mock_path_exists.return_value = True

        with patch('builtins.open') as m_open:
            self.setup.setup_machine_id()
            m_open.assert_called_once_with('root_dir/etc/machine-id', 'w')

        mock_path_exists.return_value = False

        self.setup.setup_machine_id()

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.which')
    def test_setup_permissions(self, mock_path_which, mock_command):
        mock_path_which.return_value = 'chkstat'
        self.setup.setup_permissions()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'chkstat', '--system', '--set'])
        mock_path_which.return_value = None
        with self._caplog.at_level(logging.WARNING):
            self.setup.setup_permissions()

    @patch('kiwi.system.setup.Command.run')
    def test_export_package_list_dpkg(self, mock_command):
        command = Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = Mock(return_value='apt')

        with patch('builtins.open') as m_open:
            result = self.setup.export_package_list('target_dir')
            m_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.packages',
                'w',
                encoding='utf-8')

        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W', '-f',
            '${Package}|None|${Version}|None|${Architecture}|None|None\\n'
        ])

    @patch('kiwi.system.setup.Command.run')
    def test_export_package_list_pacman(self, mock_command):
        command = Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = Mock(return_value='pacman')

        with patch('builtins.open') as m_open:
            result = self.setup.export_package_list('target_dir')
            m_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.packages', 'w')

        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with(
            ['pacman', '--query', '--dbpath', 'root_dir/var/lib/pacman'])

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.RpmDataBase')
    @patch('kiwi.system.setup.MountManager')
    def test_export_package_changes_rpm(self, mock_MountManager,
                                        mock_RpmDataBase, mock_command):
        rpmdb = Mock()
        rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath'
        rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath'
        rpmdb.has_rpm.return_value = True
        mock_RpmDataBase.return_value = rpmdb
        command = Mock()
        command.output = 'package|\nchanges'
        mock_command.return_value = command

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            result = self.setup.export_package_changes('target_dir')
            mock_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.changes',
                'w',
                encoding='utf-8')

        assert result == 'target_dir/some-image.x86_64-1.2.3.changes'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|\\n',
            '--changelog', '--dbpath', 'image_dbpath'
        ])
        self.runtime_config.get_package_changes.assert_called_once_with()

    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    @patch('os.listdir')
    def test_export_package_changes_dpkg(self, mock_os_listdir,
                                         mock_os_path_exists, mock_command):
        command = Mock()
        command.output = 'changes'
        mock_command.return_value = command
        self.xml_state.get_package_manager = Mock(return_value='apt')
        mock_os_listdir.return_value = ['package_b', 'package_a']
        mock_os_path_exists.return_value = True

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            result = self.setup.export_package_changes('target_dir')
            file_handle = mock_open.return_value.__enter__.return_value
            mock_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.changes',
                'w',
                encoding='utf-8')
            assert result == 'target_dir/some-image.x86_64-1.2.3.changes'
            assert file_handle.write.call_args_list == [
                call('package_a|\n'),
                call('changes\n'),
                call('package_b|\n'),
                call('changes\n')
            ]
        self.runtime_config.get_package_changes.assert_called_once_with()

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.RpmDataBase')
    @patch('kiwi.system.setup.MountManager')
    def test_export_package_verification(self, mock_MountManager,
                                         mock_RpmDataBase, mock_command):
        is_mounted_return = [True, False]

        def is_mounted():
            return is_mounted_return.pop()

        shared_mount = Mock()
        shared_mount.is_mounted.side_effect = is_mounted
        mock_MountManager.return_value = shared_mount
        rpmdb = Mock()
        rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath'
        rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath'
        rpmdb.has_rpm.return_value = True
        mock_RpmDataBase.return_value = rpmdb
        command = Mock()
        command.output = 'verification_data'
        mock_command.return_value = command

        with patch('builtins.open') as m_open:
            result = self.setup.export_package_verification('target_dir')
            m_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.verified',
                'w',
                encoding='utf-8')

        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'image_dbpath'
        ],
                                             raise_on_error=False)

        mock_MountManager.assert_called_once_with(device='/dev',
                                                  mountpoint='root_dir/dev')
        shared_mount.bind_mount.assert_called_once_with()
        shared_mount.umount_lazy.assert_called_once_with()
        rpmdb.has_rpm.return_value = False
        is_mounted_return = [True, False]
        mock_command.reset_mock()
        shared_mount.reset_mock()

        with patch('builtins.open'):
            result = self.setup.export_package_verification('target_dir')

        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'host_dbpath'
        ],
                                             raise_on_error=False)
        shared_mount.bind_mount.assert_called_once_with()
        shared_mount.umount_lazy.assert_called_once_with()

    @patch('kiwi.system.setup.Command.run')
    def test_export_package_verification_dpkg(self, mock_command):
        command = Mock()
        command.output = 'verification_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = Mock(return_value='apt')

        with patch('builtins.open') as m_open:
            result = self.setup.export_package_verification('target_dir')
            m_open.assert_called_once_with(
                'target_dir/some-image.x86_64-1.2.3.verified',
                'w',
                encoding='utf-8')

        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(command=[
            'dpkg', '--root', 'root_dir', '-V', '--verify-format', 'rpm'
        ],
                                             raise_on_error=False)

    @patch('kiwi.system.setup.Command.run')
    def test_set_selinux_file_contexts(self, mock_command):
        self.setup.set_selinux_file_contexts('security_context_file')
        mock_command.assert_called_once_with([
            'chroot', 'root_dir', 'setfiles', 'security_context_file', '/',
            '-v'
        ])

    @patch('kiwi.system.setup.Repository.new')
    @patch('kiwi.system.setup.Uri')
    def test_import_repositories_marked_as_imageinclude(
            self, mock_uri, mock_repo):
        uri = Mock()
        mock_uri.return_value = uri
        uri.translate = Mock(return_value="uri")
        uri.alias = Mock(return_value="uri-alias")
        uri.credentials_file_name = Mock(return_value='kiwiRepoCredentials')
        mock_uri.return_value = uri
        repo = Mock()
        mock_repo.return_value = repo
        self.setup_with_real_xml.import_repositories_marked_as_imageinclude()
        assert repo.add_repo.call_args_list[0] == call(
            'uri-alias', 'uri', 'rpm-md', None, None, None, None, None,
            'kiwiRepoCredentials', None, None, None, False, '../data/script')

    @patch('os.path.exists')
    def test_script_exists(self, mock_path_exists):
        assert self.setup.script_exists('some-script') == \
            mock_path_exists.return_value
Ejemplo n.º 7
0
    def process(self):
        """
        Build a system image from the specified description. The
        build command combines the prepare and create commands
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        abs_target_dir_path = os.path.abspath(
            self.command_args['--target-dir']
        )
        build_dir = os.sep.join([abs_target_dir_path, 'build'])
        image_root = os.sep.join([build_dir, 'image-root'])
        Path.create(build_dir)

        if not self.global_args['--logfile']:
            log.set_logfile(
                os.sep.join([abs_target_dir_path, 'build', 'image-root.log'])
            )

        self.load_xml_description(
            self.command_args['--description']
        )
        self.runtime_checker.check_efi_mode_for_disk_overlay_correctly_setup()
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_xen_uniquely_setup_as_server_or_guest()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_target_dir_path
        )
        self.runtime_checker.check_mediacheck_only_for_x86_arch()
        self.runtime_checker.check_dracut_module_for_live_iso_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_overlay_in_package_list()

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.quintuple_token(self.command_args['--set-repo'])
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(
                    *self.quintuple_token(add_repo)
                )

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.runtime_checker.check_repositories_configured()
        self.runtime_checker.check_image_include_repos_publicly_resolvable()

        package_requests = False
        if self.command_args['--add-package']:
            package_requests = True
        if self.command_args['--delete-package']:
            package_requests = True

        log.info('Preparing new root system')
        system = SystemPrepare(
            self.xml_state,
            image_root,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(manager)
        system.install_system(
            manager
        )
        if package_requests:
            if self.command_args['--add-package']:
                system.install_packages(
                    manager, self.command_args['--add-package']
                )
            if self.command_args['--delete-package']:
                system.delete_packages(
                    manager, self.command_args['--delete-package']
                )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(
            self.xml_state, image_root
        )
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()

        system.pinch_system(
            manager=manager, force=True
        )
        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # make sure system instance is cleaned up now
        del system

        setup.call_image_script()

        # make sure setup instance is cleaned up now
        del setup

        log.info('Creating system image')
        image_builder = ImageBuilder(
            self.xml_state,
            abs_target_dir_path,
            image_root,
            custom_args={
                'signing_keys': self.command_args['--signing-key'],
                'xz_options': self.runtime_config.get_xz_options()
            }
        )
        result = image_builder.create()
        result.print_results()
        result.dump(
            os.sep.join([abs_target_dir_path, 'kiwi.result'])
        )
Ejemplo n.º 8
0
    def process(self):
        """
        Prepare and install a new system for chroot access
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        self.load_xml_description(self.command_args['--description'],
                                  self.global_args['--kiwi-file'])

        abs_root_path = os.path.abspath(self.command_args['--root'])

        prepare_checks = self.checks_before_command_args
        prepare_checks.update(
            {'check_target_directory_not_in_shared_cache': [abs_root_path]})
        self.run_checks(prepare_checks)

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo']))

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(*self.sextuple_token(add_repo))

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag'])

        if self.command_args['--add-container-label']:
            for add_label in self.command_args['--add-container-label']:
                try:
                    (name, value) = add_label.split('=', 1)
                    self.xml_state.add_container_config_label(name, value)
                except Exception:
                    log.warning('Container label {0} ignored. Invalid format: '
                                'expected labelname=value'.format(add_label))

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from'])

        self.run_checks(self.checks_after_command_args)

        log.info('Preparing system')
        system = SystemPrepare(self.xml_state, abs_root_path,
                               self.command_args['--allow-existing-root'])
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key'] +
            self.xml_state.get_repositories_signing_keys(),
            self.global_args['--target-arch'])
        run_bootstrap = True
        if self.xml_state.get_package_manager() == 'apt' and \
           self.command_args['--allow-existing-root']:
            # try to call apt-get inside of the existing root.
            # If the call succeeds we skip calling debootstrap again
            # and assume the root to be ok to proceed with apt-get
            # if it fails, treat the root as dirty and give the
            # bootstrap a try
            try:
                Command.run(['chroot', abs_root_path, 'apt-get', '--version'])
                run_bootstrap = False
                log.warning('debootstrap will only be called once, skipped')
            except Exception:
                run_bootstrap = True

        if run_bootstrap:
            system.install_bootstrap(
                manager, self.command_args['--add-bootstrap-package'])

        setup = SystemSetup(self.xml_state, abs_root_path)
        setup.import_description()

        # call post_bootstrap.sh script if present
        setup.call_post_bootstrap_script()

        system.install_system(manager)

        if self.command_args['--add-package']:
            system.install_packages(manager,
                                    self.command_args['--add-package'])
        if self.command_args['--delete-package']:
            system.delete_packages(manager,
                                   self.command_args['--delete-package'])

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        profile.create(Defaults.get_profile_file(abs_root_path))

        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()
        setup.setup_permissions()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()

        # call config.sh script if present
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # delete any custom rpm macros created
        system.clean_package_manager_leftovers()

        # make sure system instance is cleaned up now
        del system
Ejemplo n.º 9
0
    def create_disk(self):  # noqa: C901
        """
        Build a bootable raw disk image

        :raises KiwiInstallMediaError:
            if install media is required and image type is not oem
        :raises KiwiVolumeManagerSetupError:
            root overlay at the same time volumes are defined is not supported

        :return: result

        :rtype: instance of :class:`Result`
        """
        if self.install_media and self.build_type_name != 'oem':
            raise KiwiInstallMediaError(
                'Install media requires oem type setup, got %s' %
                self.build_type_name)

        if self.root_filesystem_is_overlay and self.volume_manager_name:
            raise KiwiVolumeManagerSetupError(
                'Volume management together with root overlay is not supported'
            )

        # setup recovery archive, cleanup and create archive if requested
        self.system_setup.create_recovery_archive()

        # prepare boot(initrd) root system
        log.info('Preparing boot system')
        self.boot_image.prepare()

        # precalculate needed disk size
        disksize_mbytes = self.disk_setup.get_disksize_mbytes()

        # create the disk
        log.info('Creating raw disk image %s', self.diskname)
        self.loop_provider = LoopDevice(self.diskname, disksize_mbytes,
                                        self.blocksize)
        self.loop_provider.create()

        self.disk = Disk(self.firmware.get_partition_table_type(),
                         self.loop_provider,
                         self.xml_state.get_disk_start_sector())

        # create the bootloader instance
        self.bootloader_config = BootLoaderConfig(
            self.bootloader,
            self.xml_state,
            root_dir=self.root_dir,
            boot_dir=self.root_dir,
            custom_args={
                'targetbase':
                self.loop_provider.get_device(),
                'grub_directory_name':
                Defaults.get_grub_boot_directory_name(self.root_dir),
                'boot_is_crypto':
                self.boot_is_crypto
            })

        # create disk partitions and instance device map
        device_map = self._build_and_map_disk_partitions(disksize_mbytes)

        # create raid on current root device if requested
        if self.mdraid:
            self.raid_root = RaidDevice(device_map['root'])
            self.raid_root.create_degraded_raid(raid_level=self.mdraid)
            device_map['root'] = self.raid_root.get_device()
            self.disk.public_partition_id_map['kiwi_RaidPart'] = \
                self.disk.public_partition_id_map['kiwi_RootPart']
            self.disk.public_partition_id_map['kiwi_RaidDev'] = \
                device_map['root'].get_device()

        # create luks on current root device if requested
        if self.luks:
            self.luks_root = LuksDevice(device_map['root'])
            self.luks_boot_keyname = '/.root.keyfile'
            self.luks_boot_keyfile = ''.join(
                [self.root_dir, self.luks_boot_keyname])
            self.luks_root.create_crypto_luks(passphrase=self.luks,
                                              os=self.luks_os,
                                              keyfile=self.luks_boot_keyfile
                                              if self.boot_is_crypto else None)
            if self.boot_is_crypto:
                self.luks_boot_keyfile_setup = ''.join(
                    [self.root_dir, '/etc/dracut.conf.d/99-luks-boot.conf'])
                self.boot_image.write_system_config_file(
                    config={'install_items': [self.luks_boot_keyname]},
                    config_file=self.luks_boot_keyfile_setup)
                self.boot_image.include_file(
                    os.sep + os.path.basename(self.luks_boot_keyfile))
            device_map['luks_root'] = device_map['root']
            device_map['root'] = self.luks_root.get_device()

        # create spare filesystem on spare partition if present
        self._build_spare_filesystem(device_map)

        # create filesystems on boot partition(s) if any
        self._build_boot_filesystems(device_map)

        # create volumes and filesystems for root system
        if self.volume_manager_name:
            volume_manager_custom_parameters = {
                'fs_mount_options':
                self.custom_root_mount_args,
                'fs_create_options':
                self.custom_root_creation_args,
                'root_label':
                self.disk_setup.get_root_label(),
                'root_is_snapshot':
                self.xml_state.build_type.get_btrfs_root_is_snapshot(),
                'root_is_readonly_snapshot':
                self.xml_state.build_type.get_btrfs_root_is_readonly_snapshot(
                ),
                'quota_groups':
                self.xml_state.build_type.get_btrfs_quota_groups(),
                'image_type':
                self.xml_state.get_build_type_name()
            }
            self.volume_manager = VolumeManager(
                self.volume_manager_name, device_map, self.root_dir + '/',
                self.volumes, volume_manager_custom_parameters)
            self.volume_manager.setup(self.volume_group_name)
            self.volume_manager.create_volumes(self.requested_filesystem)
            self.volume_manager.mount_volumes()
            self.system = self.volume_manager
            device_map['root'] = self.volume_manager.get_device().get('root')
            device_map['swap'] = self.volume_manager.get_device().get('swap')
        else:
            log.info('Creating root(%s) filesystem on %s',
                     self.requested_filesystem,
                     device_map['root'].get_device())
            filesystem_custom_parameters = {
                'mount_options': self.custom_root_mount_args,
                'create_options': self.custom_root_creation_args
            }
            filesystem = FileSystem.new(self.requested_filesystem,
                                        device_map['root'],
                                        self.root_dir + '/',
                                        filesystem_custom_parameters)
            filesystem.create_on_device(label=self.disk_setup.get_root_label())
            self.system = filesystem

        # create swap on current root device if requested
        if self.swap_mbytes:
            swap = FileSystem.new('swap', device_map['swap'])
            swap.create_on_device(label='SWAP')

        # store root partition/filesystem uuid for profile
        self._preserve_root_partition_uuid(device_map)
        self._preserve_root_filesystem_uuid(device_map)

        # create a random image identifier
        self.mbrid = SystemIdentifier()
        self.mbrid.calculate_id()

        # create first stage metadata to boot image
        self._write_partition_id_config_to_boot_image()

        self._write_recovery_metadata_to_boot_image()

        self._write_raid_config_to_boot_image()

        self._write_generic_fstab_to_boot_image(device_map)

        self.system_setup.export_modprobe_setup(
            self.boot_image.boot_root_directory)

        # create first stage metadata to system image
        self._write_image_identifier_to_system_image()

        self._write_crypttab_to_system_image()

        self._write_generic_fstab_to_system_image(device_map)

        if self.initrd_system == 'dracut':
            if self.root_filesystem_is_multipath is False:
                self.boot_image.omit_module('multipath')
            if self.root_filesystem_is_overlay:
                self.boot_image.include_module('kiwi-overlay')
                self.boot_image.write_system_config_file(
                    config={'modules': ['kiwi-overlay']})
            if self.build_type_name == 'oem':
                self.boot_image.include_module('kiwi-repart')

        # create initrd cpio archive
        self.boot_image.create_initrd(self.mbrid)

        # create second stage metadata to system image
        self._copy_first_boot_files_to_system_image()

        self._write_bootloader_meta_data_to_system_image(device_map)

        self.mbrid.write_to_disk(self.disk.storage_provider)

        # set SELinux file security contexts if context exists
        self._setup_selinux_file_contexts()

        # syncing system data to disk image
        log.info('Syncing system to image')
        if self.system_spare:
            self.system_spare.sync_data()

        if self.system_efi:
            log.info('--> Syncing EFI boot data to EFI partition')
            self.system_efi.sync_data()

        if self.system_boot:
            log.info('--> Syncing boot data at extra partition')
            self.system_boot.sync_data(
                self._get_exclude_list_for_boot_data_sync())

        log.info('--> Syncing root filesystem data')
        if self.root_filesystem_is_overlay:
            squashed_root_file = NamedTemporaryFile()
            squashed_root = FileSystemSquashFs(
                device_provider=None,
                root_dir=self.root_dir,
                custom_args={
                    'compression':
                    self.xml_state.build_type.get_squashfscompression()
                })
            squashed_root.create_on_file(
                filename=squashed_root_file.name,
                exclude=self._get_exclude_list_for_root_data_sync(device_map))
            Command.run([
                'dd',
                'if=%s' % squashed_root_file.name,
                'of=%s' % device_map['readonly'].get_device()
            ])
        else:
            self.system.sync_data(
                self._get_exclude_list_for_root_data_sync(device_map))

        # run post sync script hook
        if self.system_setup.script_exists(defaults.POST_DISK_SYNC_SCRIPT):
            disk_system = SystemSetup(self.xml_state,
                                      self.system.get_mountpoint())
            disk_system.import_description()
            disk_system.call_disk_script()
            disk_system.cleanup()

        # install boot loader
        self._install_bootloader(device_map)

        # set root filesystem properties
        self._setup_property_root_is_readonly_snapshot()

        # prepare for install media if requested
        if self.install_media:
            log.info('Saving boot image instance to file')
            self.boot_image.dump(self.target_dir + '/boot_image.pickledump')

        self.result.verify_image_size(
            self.runtime_config.get_max_size_constraint(), self.diskname)
        # store image file name in result
        self.result.add(
            key='disk_image',
            filename=self.diskname,
            use_for_bundle=True if not self.image_format else False,
            compress=self.runtime_config.get_bundle_compression(default=True),
            shasum=True)

        # create image root metadata
        self.result.add(key='image_packages',
                        filename=self.system_setup.export_package_list(
                            self.target_dir),
                        use_for_bundle=True,
                        compress=False,
                        shasum=False)
        self.result.add(key='image_verified',
                        filename=self.system_setup.export_package_verification(
                            self.target_dir),
                        use_for_bundle=True,
                        compress=False,
                        shasum=False)

        return self.result
Ejemplo n.º 10
0
    def process(self):                                      # noqa: C901
        """
        Prepare and install a new system for chroot access
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        self.load_xml_description(
            self.command_args['--description']
        )

        abs_root_path = os.path.abspath(self.command_args['--root'])

        self.runtime_checker.check_efi_mode_for_disk_overlay_correctly_setup()
        self.runtime_checker.check_grub_efi_installed_for_efi_firmware()
        self.runtime_checker.check_boot_description_exists()
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_xen_uniquely_setup_as_server_or_guest()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_root_path
        )
        self.runtime_checker.check_mediacheck_only_for_x86_arch()
        self.runtime_checker.check_dracut_module_for_live_iso_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_overlay_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_oem_in_package_list()
        self.runtime_checker.check_dracut_module_for_oem_install_in_package_list()

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo'])
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(
                    *self.sextuple_token(add_repo)
                )

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.runtime_checker.check_repositories_configured()
        self.runtime_checker.check_image_include_repos_publicly_resolvable()

        package_requests = False
        if self.command_args['--add-package']:
            package_requests = True
        if self.command_args['--delete-package']:
            package_requests = True

        log.info('Preparing system')
        system = SystemPrepare(
            self.xml_state,
            abs_root_path,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(manager)
        system.install_system(
            manager
        )
        if package_requests:
            if self.command_args['--add-package']:
                system.install_packages(
                    manager, self.command_args['--add-package']
                )
            if self.command_args['--delete-package']:
                system.delete_packages(
                    manager, self.command_args['--delete-package']
                )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(
            self.xml_state, abs_root_path
        )
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()

        system.pinch_system(
            manager=manager, force=True
        )

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # make sure system instance is cleaned up now
        del system
Ejemplo n.º 11
0
    def create_disk(self) -> Result:
        """
        Build a bootable raw disk image

        :raises KiwiInstallMediaError:
            if install media is required and image type is not oem
        :raises KiwiVolumeManagerSetupError:
            root overlay at the same time volumes are defined is not supported

        :return: result

        :rtype: instance of :class:`Result`
        """
        # an instance of a class with the sync_data capability
        # representing the entire image system except for the boot/ area
        # which could live on another part of the disk
        system: Any = None

        # an instance of a class with the sync_data capability
        # representing the boot/ area of the disk if not part of
        # self.system
        system_boot: Optional[FileSystemBase] = None

        # an instance of a class with the sync_data capability
        # representing the boot/efi area of the disk
        system_efi: Optional[FileSystemBase] = None

        # an instance of a class with the sync_data capability
        # representing the spare_part_mountpoint area of the disk
        system_spare: Optional[FileSystemBase] = None

        # a list of instances with the sync_data capability
        # representing the custom partitions area of the disk
        system_custom_parts: List[FileSystemBase] = []

        if self.install_media and self.build_type_name != 'oem':
            raise KiwiInstallMediaError(
                'Install media requires oem type setup, got {0}'.format(
                    self.build_type_name))

        if self.root_filesystem_is_overlay and self.volume_manager_name:
            raise KiwiVolumeManagerSetupError(
                'Volume management together with root overlay is not supported'
            )

        # setup recovery archive, cleanup and create archive if requested
        self.system_setup.create_recovery_archive()

        # prepare initrd
        if self.boot_image.has_initrd_support():
            log.info('Preparing boot system')
            self.boot_image.prepare()

        # precalculate needed disk size
        disksize_mbytes = self.disk_setup.get_disksize_mbytes()

        # create the disk
        log.info('Creating raw disk image %s', self.diskname)
        loop_provider = LoopDevice(self.diskname, disksize_mbytes,
                                   self.blocksize)
        loop_provider.create()

        disk = Disk(self.firmware.get_partition_table_type(), loop_provider,
                    self.xml_state.get_disk_start_sector())

        # create the bootloader instance
        if self.bootloader != 'custom':
            self.bootloader_config = BootLoaderConfig.new(
                self.bootloader,
                self.xml_state,
                root_dir=self.root_dir,
                boot_dir=self.root_dir,
                custom_args={
                    'targetbase':
                    loop_provider.get_device(),
                    'grub_directory_name':
                    Defaults.get_grub_boot_directory_name(self.root_dir),
                    'crypto_disk':
                    True if self.luks is not None else False,
                    'boot_is_crypto':
                    self.boot_is_crypto
                })

        # create disk partitions and instance device map
        device_map = self._build_and_map_disk_partitions(disk, disksize_mbytes)

        # create raid on current root device if requested
        raid_root = None
        if self.mdraid:
            raid_root = RaidDevice(device_map['root'])
            raid_root.create_degraded_raid(raid_level=self.mdraid)
            device_map['root'] = raid_root.get_device()
            disk.public_partition_id_map['kiwi_RaidPart'] = \
                disk.public_partition_id_map['kiwi_RootPart']
            disk.public_partition_id_map['kiwi_RaidDev'] = \
                device_map['root'].get_device()

        # create luks on current root device if requested
        luks_root = None
        if self.luks is not None:
            luks_root = LuksDevice(device_map['root'])
            self.luks_boot_keyname = '/.root.keyfile'
            self.luks_boot_keyfile = ''.join(
                [self.root_dir, self.luks_boot_keyname])
            # use LUKS key file for the following conditions:
            # 1. /boot is encrypted
            #    In this case grub needs to read from LUKS via the
            #    cryptodisk module which at the moment always asks
            #    for the passphrase even when empty. The keyfile
            #    setup makes sure only one interaction on the grub
            #    stage is needed
            # 2. LUKS passphrase is configured as empty string
            #    In this case the keyfile allows to open the
            #    LUKS pool without asking
            #
            luks_need_keyfile = \
                True if self.boot_is_crypto or self.luks == '' else False
            luks_root.create_crypto_luks(
                passphrase=self.luks,
                os=self.luks_os,
                keyfile=self.luks_boot_keyfile if luks_need_keyfile else '')
            if luks_need_keyfile:
                self.luks_boot_keyfile_setup = ''.join(
                    [self.root_dir, '/etc/dracut.conf.d/99-luks-boot.conf'])
                self.boot_image.write_system_config_file(
                    config={'install_items': [self.luks_boot_keyname]},
                    config_file=self.luks_boot_keyfile_setup)
                self.boot_image.include_file(
                    os.sep + os.path.basename(self.luks_boot_keyfile))
            device_map['luks_root'] = device_map['root']
            device_map['root'] = luks_root.get_device()

        # create spare filesystem on spare partition if present
        system_spare = self._build_spare_filesystem(device_map)

        system_custom_parts = self._build_custom_parts_filesystem(
            device_map, self.custom_partitions)

        # create filesystems on boot partition(s) if any
        system_boot, system_efi = self._build_boot_filesystems(device_map)

        # create volumes and filesystems for root system
        if self.volume_manager_name:
            volume_manager_custom_parameters = {
                'fs_mount_options':
                self.custom_root_mount_args,
                'fs_create_options':
                self.custom_root_creation_args,
                'root_label':
                self.disk_setup.get_root_label(),
                'root_is_snapshot':
                self.xml_state.build_type.get_btrfs_root_is_snapshot(),
                'root_is_readonly_snapshot':
                self.xml_state.build_type.get_btrfs_root_is_readonly_snapshot(
                ),
                'quota_groups':
                self.xml_state.build_type.get_btrfs_quota_groups(),
                'resize_on_boot':
                self.disk_resize_requested
            }
            volume_manager = VolumeManager.new(
                self.volume_manager_name, device_map, self.root_dir + '/',
                self.volumes, volume_manager_custom_parameters)
            volume_manager.setup(self.volume_group_name)
            volume_manager.create_volumes(self.requested_filesystem)
            volume_manager.mount_volumes()
            system = volume_manager
            device_map['root'] = volume_manager.get_device().get('root')
            device_map['swap'] = volume_manager.get_device().get('swap')
        else:
            log.info('Creating root(%s) filesystem on %s',
                     self.requested_filesystem,
                     device_map['root'].get_device())
            filesystem_custom_parameters = {
                'mount_options': self.custom_root_mount_args,
                'create_options': self.custom_root_creation_args
            }
            filesystem = FileSystem.new(self.requested_filesystem,
                                        device_map['root'],
                                        self.root_dir + '/',
                                        filesystem_custom_parameters)
            filesystem.create_on_device(label=self.disk_setup.get_root_label())
            system = filesystem

        # create swap on current root device if requested
        if self.swap_mbytes:
            swap = FileSystem.new('swap', device_map['swap'])
            swap.create_on_device(label='SWAP')

        # store root partition/filesystem uuid for profile
        self._preserve_root_partition_uuid(device_map)
        self._preserve_root_filesystem_uuid(device_map)

        # create a random image identifier
        self.mbrid = SystemIdentifier()
        self.mbrid.calculate_id()

        # create first stage metadata to boot image
        self._write_partition_id_config_to_boot_image(disk)

        self._write_recovery_metadata_to_boot_image()

        self._write_raid_config_to_boot_image(raid_root)

        self._write_generic_fstab_to_boot_image(device_map, system)

        self.system_setup.export_modprobe_setup(
            self.boot_image.boot_root_directory)

        # create first stage metadata to system image
        self._write_image_identifier_to_system_image()

        self._write_crypttab_to_system_image(luks_root)

        self._write_generic_fstab_to_system_image(device_map, system)

        if self.initrd_system == 'dracut':
            if self.root_filesystem_is_multipath is False:
                self.boot_image.omit_module('multipath')
            if self.root_filesystem_is_overlay:
                self.boot_image.include_module('kiwi-overlay')
                self.boot_image.write_system_config_file(
                    config={'modules': ['kiwi-overlay']})
            if self.disk_resize_requested:
                self.boot_image.include_module('kiwi-repart')

        # create initrd
        if self.boot_image.has_initrd_support():
            self.boot_image.create_initrd(self.mbrid)

        # create second stage metadata to system image
        self._copy_first_boot_files_to_system_image()

        self._write_bootloader_meta_data_to_system_image(device_map, disk)

        self.mbrid.write_to_disk(disk.storage_provider)

        # set SELinux file security contexts if context exists
        self._setup_selinux_file_contexts()

        # syncing system data to disk image
        self._sync_system_to_image(device_map, system, system_boot, system_efi,
                                   system_spare, system_custom_parts)

        # run post sync script hook
        if self.system_setup.script_exists(defaults.POST_DISK_SYNC_SCRIPT):
            disk_system = SystemSetup(self.xml_state, system.get_mountpoint())
            disk_system.import_description()
            disk_system.call_disk_script()
            disk_system.cleanup()

        # install boot loader
        self._install_bootloader(device_map, disk, system)

        # set root filesystem properties
        self._setup_property_root_is_readonly_snapshot(system)

        Result.verify_image_size(self.runtime_config.get_max_size_constraint(),
                                 self.diskname)
        # store image bundle_format in result
        if self.bundle_format:
            self.result.add_bundle_format(self.bundle_format)

        # store image file name in result
        compression = self.runtime_config.get_bundle_compression(default=True)
        if self.luks is not None:
            compression = False
        self.result.add(
            key='disk_image',
            filename=self.diskname,
            use_for_bundle=True if not self.image_format else False,
            compress=compression,
            shasum=True)

        # create image root metadata
        self.result.add(key='image_packages',
                        filename=self.system_setup.export_package_list(
                            self.target_dir),
                        use_for_bundle=True,
                        compress=False,
                        shasum=False)
        self.result.add(key='image_changes',
                        filename=self.system_setup.export_package_changes(
                            self.target_dir),
                        use_for_bundle=True,
                        compress=True,
                        shasum=False)
        self.result.add(key='image_verified',
                        filename=self.system_setup.export_package_verification(
                            self.target_dir),
                        use_for_bundle=True,
                        compress=False,
                        shasum=False)

        return self.result
Ejemplo n.º 12
0
    def process(self):
        """
        Build a system image from the specified description. The
        build command combines the prepare and create commands
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        abs_target_dir_path = os.path.abspath(
            self.command_args['--target-dir']
        )
        build_dir = os.sep.join([abs_target_dir_path, 'build'])
        image_root = os.sep.join([build_dir, 'image-root'])
        Path.create(build_dir)

        if not self.global_args['--logfile']:
            log.set_logfile(
                os.sep.join([abs_target_dir_path, 'build', 'image-root.log'])
            )

        self.load_xml_description(
            self.command_args['--description']
        )
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_boot_image_reference_correctly_setup()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_image_include_repos_http_resolvable()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_target_dir_path
        )

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()

        if self.command_args['--set-repo']:
            (repo_source, repo_type, repo_alias, repo_prio) = \
                self.quadruple_token(self.command_args['--set-repo'])
            self.xml_state.set_repository(
                repo_source, repo_type, repo_alias, repo_prio
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                (repo_source, repo_type, repo_alias, repo_prio) = \
                    self.quadruple_token(add_repo)
                self.xml_state.add_repository(
                    repo_source, repo_type, repo_alias, repo_prio
                )

                Path.create(abs_target_dir_path)

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.runtime_checker.check_repositories_configured()

        if Defaults.is_obs_worker():
            # This build runs inside of a buildservice worker. Therefore
            # the repo defintions is adapted accordingly
            self.xml_state.translate_obs_to_suse_repositories()

        elif self.command_args['--obs-repo-internal']:
            # This build should use the internal SUSE buildservice
            # Be aware that the buildhost has to provide access
            self.xml_state.translate_obs_to_ibs_repositories()

        package_requests = False
        if self.command_args['--add-package']:
            package_requests = True
        if self.command_args['--delete-package']:
            package_requests = True

        log.info('Preparing new root system')
        system = SystemPrepare(
            self.xml_state,
            image_root,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(manager)
        system.install_system(
            manager
        )
        if package_requests:
            if self.command_args['--add-package']:
                system.install_packages(
                    manager, self.command_args['--add-package']
                )
            if self.command_args['--delete-package']:
                system.delete_packages(
                    manager, self.command_args['--delete-package']
                )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(
            self.xml_state, image_root
        )
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_timezone()

        system.pinch_system(
            manager=manager, force=True
        )
        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        if self.xml_state.has_repositories_marked_as_imageinclude():
            setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # make sure system instance is cleaned up now
        del system

        setup.call_image_script()

        # make sure setup instance is cleaned up now
        del setup

        log.info('Creating system image')
        image_builder = ImageBuilder(
            self.xml_state,
            abs_target_dir_path,
            image_root,
            {'signing_keys': self.command_args['--signing-key']}
        )
        result = image_builder.create()
        result.print_results()
        result.dump(
            os.sep.join([abs_target_dir_path, 'kiwi.result'])
        )
Ejemplo n.º 13
0
    def process(self):                                      # noqa: C901
        """
        Build a system image from the specified description. The
        build command combines the prepare and create commands
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        abs_target_dir_path = os.path.abspath(
            self.command_args['--target-dir']
        )
        build_dir = os.sep.join([abs_target_dir_path, 'build'])
        image_root = os.sep.join([build_dir, 'image-root'])
        Path.create(build_dir)

        if not self.global_args['--logfile']:
            log.set_logfile(
                os.sep.join([abs_target_dir_path, 'build', 'image-root.log'])
            )

        self.load_xml_description(
            self.command_args['--description']
        )
        self.runtime_checker.check_efi_mode_for_disk_overlay_correctly_setup()
        self.runtime_checker.check_boot_description_exists()
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_xen_uniquely_setup_as_server_or_guest()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_target_dir_path
        )
        self.runtime_checker.check_mediacheck_only_for_x86_arch()
        self.runtime_checker.check_dracut_module_for_live_iso_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_overlay_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_oem_in_package_list()
        self.runtime_checker.check_dracut_module_for_oem_install_in_package_list()

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo'])
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(
                    *self.sextuple_token(add_repo)
                )

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.runtime_checker.check_repositories_configured()
        self.runtime_checker.check_image_include_repos_publicly_resolvable()

        log.info('Preparing new root system')
        system = SystemPrepare(
            self.xml_state,
            image_root,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(manager)
        system.install_system(
            manager
        )
        if self.command_args['--add-package']:
            system.install_packages(
                manager, self.command_args['--add-package']
            )
        if self.command_args['--delete-package']:
            system.delete_packages(
                manager, self.command_args['--delete-package']
            )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(
            self.xml_state, image_root
        )
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # make sure system instance is cleaned up now
        del system

        setup.call_image_script()

        # make sure setup instance is cleaned up now
        del setup

        log.info('Creating system image')
        image_builder = ImageBuilder(
            self.xml_state,
            abs_target_dir_path,
            image_root,
            custom_args={
                'signing_keys': self.command_args['--signing-key'],
                'xz_options': self.runtime_config.get_xz_options()
            }
        )
        result = image_builder.create()
        result.print_results()
        result.dump(
            os.sep.join([abs_target_dir_path, 'kiwi.result'])
        )
Ejemplo n.º 14
0
class TestSystemSetup(object):
    @patch('platform.machine')
    def setup(self, mock_machine):
        mock_machine.return_value = 'x86_64'
        self.context_manager_mock = mock.Mock()
        self.file_mock = mock.Mock()
        self.enter_mock = mock.Mock()
        self.exit_mock = mock.Mock()
        self.enter_mock.return_value = self.file_mock
        setattr(self.context_manager_mock, '__enter__', self.enter_mock)
        setattr(self.context_manager_mock, '__exit__', self.exit_mock)
        self.xml_state = mock.MagicMock()
        self.xml_state.get_package_manager = mock.Mock(
            return_value='zypper'
        )
        self.xml_state.build_type.get_filesystem = mock.Mock(
            return_value='ext3'
        )
        self.xml_state.xml_data.get_name = mock.Mock(
            return_value='some-image'
        )
        self.xml_state.get_image_version = mock.Mock(
            return_value='1.2.3'
        )
        self.xml_state.xml_data.description_dir = 'description_dir'
        self.setup = SystemSetup(
            self.xml_state, 'root_dir'
        )
        description = XMLDescription(
            description='../data/example_config.xml',
            derived_from='derived/description'
        )
        self.setup_with_real_xml = SystemSetup(
            XMLState(description.load()), 'root_dir'
        )
        command_run = namedtuple(
            'command', ['output', 'error', 'returncode']
        )
        self.run_result = command_run(
            output='password-hash\n',
            error='stderr',
            returncode=0
        )

    @patch('platform.machine')
    def test_setup_ix86(self, mock_machine):
        mock_machine.return_value = 'i686'
        setup = SystemSetup(
            mock.MagicMock(), 'root_dir'
        )
        assert setup.arch == 'ix86'

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description(self, mock_path, mock_open, mock_command):
        mock_path.return_value = True
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp', Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(['cp', '../data/bootstrap.tgz', 'root_dir/image/'])]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description_archive_from_derived(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [
            True, False, True, True, True, True, True
        ]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp', Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call([
                'cp', 'derived/description/bootstrap.tgz', 'root_dir/image/'
            ])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_editboot_scripts_not_found(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [False, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_archives_not_found(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [False, False, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    def test_cleanup(self, mock_command):
        self.setup.cleanup()
        mock_command.assert_called_once_with(
            ['rm', '-r', '-f', '/.kconfig', '/image']
        )

    @patch_open
    def test_import_shell_environment(self, mock_open):
        mock_profile = mock.MagicMock()
        mock_profile.create = mock.Mock(
            return_value=['a']
        )
        mock_open.return_value = self.context_manager_mock
        self.setup.import_shell_environment(mock_profile)
        mock_profile.create.assert_called_once_with()
        mock_open.assert_called_once_with('root_dir/.profile', 'w')
        self.file_mock.write.assert_called_once_with('a\n')

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_copy_links(
        self, mock_os_path, mock_DataSync, mock_command
    ):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(
            follow_links=True, preserve_owner_group=True
        )
        mock_DataSync.assert_called_once_with(
            'description_dir/root/', 'root_dir'
        )
        data.sync_data.assert_called_once_with(
            options=[
                '-r', '-p', '-t', '-D', '-H', '-X', '-A',
                '--one-file-system', '--copy-links', '-o', '-g'
            ]
        )

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_import_overlay_files_links(
        self, mock_os_path, mock_DataSync, mock_command
    ):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_os_path.return_value = True
        self.setup.import_overlay_files(
            follow_links=False, preserve_owner_group=True
        )
        mock_DataSync.assert_called_once_with(
            'description_dir/root/', 'root_dir'
        )
        data.sync_data.assert_called_once_with(
            options=[
                '-r', '-p', '-t', '-D', '-H', '-X', '-A',
                '--one-file-system', '--links', '-o', '-g'
            ]
        )

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('os.path.exists')
    def test_import_overlay_files_from_archive(
        self, mock_os_path, mock_archive
    ):
        archive = mock.Mock()
        mock_archive.return_value = archive

        exists_results = [True, False]

        def side_effect(arg):
            return exists_results.pop()

        mock_os_path.side_effect = side_effect

        self.setup.import_overlay_files()

        mock_archive.assert_called_once_with(
            'description_dir/root.tar.gz'
        )
        archive.extract.assert_called_once_with(
            'root_dir'
        )

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig', [
                'root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"'
            ]
        )

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_keyboard_map_with_systemd(
        self, mock_path, mock_run, mock_shell, mock_caps
    ):
        mock_caps.return_value = True
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--keymap=keytable'
            ])
        ])

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig', [
                'root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8'
            ]
        )

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale_with_systemd(
        self, mock_path, mock_run, mock_shell, mock_caps
    ):
        mock_caps.return_valure = True
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_run.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--locale=locale1.UTF-8'
            ])
        ])

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    def test_setup_locale_POSIX(self, mock_path, mock_run, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'POSIX,locale2'
        self.setup.setup_locale()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig', [
                'root_dir/etc/sysconfig/language', 'RC_LANG', 'POSIX'
            ]
        )

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_locale_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone(self, mock_command):
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call([
                'chroot', 'root_dir', 'ln', '-s', '-f',
                '/usr/share/zoneinfo/timezone', '/etc/localtime'
            ])
        ])

    @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone_with_systemd(self, mock_command, mock_caps):
        mock_caps.return_value = True
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_has_calls([
            call(['rm', '-r', '-f', 'root_dir/etc/localtime']),
            call([
                'chroot', 'root_dir', 'systemd-firstboot',
                '--timezone=timezone'
            ])
        ])

    @patch('kiwi.system.setup.Users')
    def test_setup_groups(self, mock_users):
        users = mock.Mock()
        users.group_exists = mock.Mock(
            return_value=False
        )
        mock_users.return_value = users

        self.setup_with_real_xml.setup_groups()

        calls = [
            call('users'),
            call('kiwi'),
            call('admin')
        ]
        users.group_exists.assert_has_calls(calls)

        calls = [
            call('users', []),
            call('kiwi', []),
            call('admin', [])
        ]
        users.group_add.assert_has_calls(calls)

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_add(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(
            return_value=False
        )
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        calls = [
            call('root'),
            call('tux'),
            call('kiwi')
        ]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call(
                'root', [
                    '-p', 'password-hash',
                    '-s', '/bin/bash',
                    '-u', '815', '-c', 'Bob',
                    '-m', '-d', '/root'
                ]
            ),
            call(
                'tux', [
                    '-p', 'password-hash',
                    '-g', 'users',
                    '-m', '-d', '/home/tux'
                ]
            ),
            call(
                'kiwi', [
                    '-p', 'password-hash',
                    '-g', 'kiwi', '-G', 'admin,users',
                    '-m', '-d', '/home/kiwi'
                ]
            )
        ]
        users.user_add.assert_has_calls(calls)

        mock_command.assert_called_with(
            ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd']
        )

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(
            return_value=True
        )
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()
        calls = [
            call('root'),
            call('tux'),
            call('kiwi')
        ]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call(
                'root', [
                    '-p', 'password-hash',
                    '-s', '/bin/bash', '-u', '815', '-c', 'Bob'
                ]
            ),
            call(
                'tux', [
                    '-p', 'password-hash',
                    '-g', 'users'
                ]
            ),
            call(
                'kiwi', [
                    '-p', 'password-hash',
                    '-g', 'kiwi', '-G', 'admin,users'
                ]
            )
        ]
        users.user_modify.assert_has_calls(calls)

    @patch('kiwi.system.setup.Path.which')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_plymouth_splash(self, mock_command, mock_which):
        mock_which.return_value = 'plymouth-set-default-theme'
        preferences = mock.Mock()
        preferences.get_bootsplash_theme = mock.Mock(
            return_value=['some-theme']
        )
        self.xml_state.get_preferences_sections = mock.Mock(
            return_value=[preferences]
        )
        self.setup.setup_plymouth_splash()
        mock_which.assert_called_once_with(
            custom_env={'PATH': 'root_dir/usr/sbin'},
            filename='plymouth-set-default-theme'
        )
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme']
        )

    @patch_open
    @patch('os.path.exists')
    def test_import_image_identifier(self, mock_os_path, mock_open):
        self.xml_state.xml_data.get_id = mock.Mock(
            return_value='42'
        )
        mock_os_path.return_value = True
        mock_open.return_value = self.context_manager_mock
        self.setup.import_image_identifier()
        mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w')
        self.file_mock.write.assert_called_once_with('42\n')

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_config_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple(
            'result', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_config_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/config.sh']
        )

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/images.sh']
        )

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_config_script(
        self, mock_abspath, mock_exists, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_config_script('ext4', 1)
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_config.sh'
        )
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh ext4 1'
        ])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_install_script(
        self, mock_abspath, mock_exists, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script(
            'my_image.raw', '/dev/mapper/loop0p1'
        )
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_install.sh'
        )
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh my_image.raw /dev/mapper/loop0p1'
        ])

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script_raises(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script_raises(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script(
            'my_image.raw', '/dev/mapper/loop0p1'
        )

    @patch('kiwi.command.Command.run')
    def test_create_init_link_from_linuxrc(self, mock_command):
        self.setup.create_init_link_from_linuxrc()
        mock_command.assert_called_once_with(
            ['ln', 'root_dir/linuxrc', 'root_dir/init']
        )

    @patch('kiwi.command.Command.run')
    def test_create_recovery_archive_cleanup_only(self, mock_command):
        self.setup.oemconfig['recovery'] = False
        self.setup.create_recovery_archive()
        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*']
        )

    @patch_open
    def test_create_fstab(self, mock_open):
        mock_open.return_value = self.context_manager_mock
        self.setup.create_fstab(['fstab_entry'])
        mock_open.assert_called_once_with(
            'root_dir/etc/fstab', 'w'
        )
        self.file_mock.write.assert_called_once_with('fstab_entry\n')

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.NamedTemporaryFile')
    @patch('kiwi.system.setup.ArchiveTar')
    @patch_open
    @patch('kiwi.system.setup.Compress')
    @patch('os.path.getsize')
    @patch('kiwi.system.setup.Path.wipe')
    def test_create_recovery_archive(
        self, mock_wipe, mock_getsize, mock_compress,
        mock_open, mock_archive, mock_temp, mock_command
    ):
        mock_open.return_value = self.context_manager_mock
        mock_getsize.return_value = 42
        compress = mock.Mock()
        mock_compress.return_value = compress
        archive = mock.Mock()
        mock_archive.return_value = archive
        tmpdir = mock.Mock()
        tmpdir.name = 'tmpdir'
        mock_temp.return_value = tmpdir
        self.setup.oemconfig['recovery'] = True
        self.setup.oemconfig['recovery_inplace'] = True

        self.setup.create_recovery_archive()

        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*']
        )
        mock_archive.assert_called_once_with(
            create_from_file_list=False, filename='tmpdir'
        )
        archive.create.assert_called_once_with(
            exclude=['dev', 'proc', 'sys'],
            options=[
                '--numeric-owner',
                '--hard-dereference',
                '--preserve-permissions'
            ],
            source_dir='root_dir'
        )
        assert mock_command.call_args_list[1] == call(
            ['mv', 'tmpdir', 'root_dir/recovery.tar']
        )
        assert mock_open.call_args_list[0] == call(
            'root_dir/recovery.tar.filesystem', 'w'
        )
        assert self.file_mock.write.call_args_list[0] == call('ext3')
        assert mock_command.call_args_list[2] == call(
            ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l']
        )
        assert mock_open.call_args_list[1] == call(
            'root_dir/recovery.tar.files', 'w'
        )
        assert mock_getsize.call_args_list[0] == call(
            'root_dir/recovery.tar'
        )
        assert self.file_mock.write.call_args_list[1] == call('1\n')
        assert mock_open.call_args_list[2] == call(
            'root_dir/recovery.tar.size', 'w'
        )
        assert self.file_mock.write.call_args_list[2] == call('42')
        mock_compress.assert_called_once_with(
            'root_dir/recovery.tar'
        )
        compress.gzip.assert_called_once_with()
        assert mock_getsize.call_args_list[1] == call(
            'root_dir/recovery.tar.gz'
        )
        assert mock_open.call_args_list[3] == call(
            'root_dir/recovery.partition.size', 'w'
        )
        assert self.file_mock.write.call_args_list[3] == call('300')
        mock_wipe.assert_called_once_with(
            'root_dir/recovery.tar.gz'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.create')
    @patch('kiwi.system.setup.DataSync')
    @patch('os.path.exists')
    def test_export_modprobe_setup(
        self, mock_exists, mock_DataSync, mock_path, mock_command
    ):
        data = mock.Mock()
        mock_DataSync.return_value = data
        mock_exists.return_value = True
        self.setup.export_modprobe_setup('target_root_dir')
        mock_path.assert_called_once_with('target_root_dir/etc')
        mock_DataSync.assert_called_once_with(
            'root_dir/etc/modprobe.d', 'target_root_dir/etc/'
        )
        data.sync_data.assert_called_once_with(
            options=['-z', '-a']
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_list_rpm(
        self, mock_open, mock_command
    ):
        command = mock.Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_has_calls([
            call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']),
            call([
                'rpm', '--root', 'root_dir', '-qa', '--qf',
                '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}\\n',
                '--dbpath', 'packages_data'
            ])
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_list_rpm_no_dbpath(
        self, mock_open, mock_command
    ):
        cmd = mock.Mock()
        cmd.output = 'packages_data'

        def dbpath_check_fails(command):
            if '%_dbpath' in command:
                raise KiwiCommandError()
            else:
                return cmd

        mock_command.side_effect = dbpath_check_fails
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_has_calls([
            call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']),
            call([
                'rpm', '--root', 'root_dir', '-qa', '--qf',
                '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}\\n'
            ])
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_list_dpkg(
        self, mock_open, mock_command
    ):
        command = mock.Mock()
        command.output = 'packages_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = mock.Mock(
            return_value='apt-get'
        )
        result = self.setup.export_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W',
            '-f', '${Package}|None|${Version}|None|${Architecture}|None\\n'
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_verification(
        self, mock_open, mock_command
    ):
        command = mock.Mock()
        command.output = 'verification_data'
        mock_command.return_value = command
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_has_calls([
            call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']),
            call(command=[
                'rpm', '--root', 'root_dir', '-Va',
                '--dbpath', 'verification_data'
            ], raise_on_error=False)
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_verification_no_dbpath(
        self, mock_open, mock_command
    ):
        cmd = mock.Mock()
        cmd.output = 'verification_data'

        def dbpath_check_fails(command, raise_on_error):
            if '%_dbpath' in command:
                raise KiwiCommandError()
            else:
                return cmd

        mock_command.side_effect = dbpath_check_fails
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_has_calls([
            call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']),
            call(
                command=['rpm', '--root', 'root_dir', '-Va'],
                raise_on_error=False
            )
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch_open
    def test_export_package_verification_dpkg(
        self, mock_open, mock_command
    ):
        command = mock.Mock()
        command.output = 'verification_data'
        mock_command.return_value = command
        self.xml_state.get_package_manager = mock.Mock(
            return_value='apt-get'
        )
        result = self.setup.export_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(
            command=[
                'dpkg', '--root', 'root_dir', '-V',
                '--verify-format', 'rpm'
            ],
            raise_on_error=False
        )
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    def test_set_selinux_file_contexts(self, mock_command):
        self.setup.set_selinux_file_contexts('security_context_file')
        mock_command.assert_called_once_with(
            [
                'chroot', 'root_dir',
                'setfiles', 'security_context_file', '/', '-v'
            ]
        )

    @patch('kiwi.system.setup.Repository')
    @patch('kiwi.system.setup.Uri')
    def test_import_repositories_marked_as_imageinclude(
        self, mock_uri, mock_repo
    ):
        uri = mock.Mock()
        mock_uri.return_value = uri
        uri.translate = mock.Mock(
            return_value="uri"
        )
        uri.alias = mock.Mock(
            return_value="uri-alias"
        )
        uri.credentials_file_name = mock.Mock(
            return_value='kiwiRepoCredentials'
        )
        mock_uri.return_value = uri
        repo = mock.Mock()
        mock_repo.return_value = repo
        self.setup_with_real_xml.import_repositories_marked_as_imageinclude()
        assert repo.add_repo.call_args_list[0] == call(
            'uri-alias', 'uri', 'rpm-md', None, None, None, None, None,
            'kiwiRepoCredentials', None, None
        )
Ejemplo n.º 15
0
class TestSystemSetup(object):
    @patch('platform.machine')
    def setup(self, mock_machine):
        mock_machine.return_value = 'x86_64'
        self.context_manager_mock = mock.Mock()
        self.file_mock = mock.Mock()
        self.enter_mock = mock.Mock()
        self.exit_mock = mock.Mock()
        self.enter_mock.return_value = self.file_mock
        setattr(self.context_manager_mock, '__enter__', self.enter_mock)
        setattr(self.context_manager_mock, '__exit__', self.exit_mock)
        self.xml_state = mock.MagicMock()
        self.xml_state.build_type.get_filesystem = mock.Mock(
            return_value='ext3')
        self.xml_state.xml_data.get_name = mock.Mock(return_value='some-image')
        self.xml_state.get_image_version = mock.Mock(return_value='1.2.3')
        self.xml_state.xml_data.description_dir = 'description_dir'
        self.setup = SystemSetup(self.xml_state, 'root_dir')
        description = XMLDescription(description='../data/example_config.xml',
                                     derived_from='derived/description')
        self.setup_with_real_xml = SystemSetup(XMLState(description.load()),
                                               'root_dir')
        command_run = namedtuple('command', ['output', 'error', 'returncode'])
        self.run_result = command_run(output='password-hash\n',
                                      error='stderr',
                                      returncode=0)

    @patch('platform.machine')
    def test_setup_ix86(self, mock_machine):
        mock_machine.return_value = 'i686'
        setup = SystemSetup(mock.MagicMock(), 'root_dir')
        assert setup.arch == 'ix86'

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description(self, mock_path, mock_open, mock_command):
        mock_path.return_value = True
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(['cp', '../data/bootstrap.tgz', 'root_dir/image/'])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description_archive_from_derived(self, mock_path,
                                                     mock_open, mock_command):
        path_return_values = [True, False, True, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp',
                Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(
                ['cp', 'derived/description/bootstrap.tgz', 'root_dir/image/'])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_editboot_scripts_not_found(
            self, mock_path, mock_open, mock_command):
        path_return_values = [False, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_archives_not_found(
            self, mock_path, mock_open, mock_command):
        path_return_values = [False, False, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    def test_cleanup(self, mock_command):
        self.setup.cleanup()
        mock_command.assert_called_once_with(
            ['rm', '-r', '-f', '/.kconfig', '/image'])

    @patch_open
    def test_import_shell_environment(self, mock_open):
        mock_profile = mock.MagicMock()
        mock_profile.create = mock.Mock(return_value=['a'])
        mock_open.return_value = self.context_manager_mock
        self.setup.import_shell_environment(mock_profile)
        mock_profile.create.assert_called_once_with()
        mock_open.assert_called_once_with('root_dir/.profile', 'w')
        self.file_mock.write.assert_called_once_with('a\n')

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_overlay_files_copy_links(self, mock_os_path, mock_command):
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=True,
                                        preserve_owner_group=True)
        mock_command.assert_called_once_with([
            'rsync', '-r', '-p', '-t', '-D', '-H', '-X', '-A',
            '--one-file-system', '--copy-links', '-o', '-g',
            'description_dir/root/', 'root_dir'
        ])

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_overlay_files_links(self, mock_os_path, mock_command):
        mock_os_path.return_value = True
        self.setup.import_overlay_files(follow_links=False,
                                        preserve_owner_group=True)
        mock_command.assert_called_once_with([
            'rsync', '-r', '-p', '-t', '-D', '-H', '-X', '-A',
            '--one-file-system', '--links', '-o', '-g',
            'description_dir/root/', 'root_dir'
        ])

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('os.path.exists')
    def test_import_overlay_files_from_archive(self, mock_os_path,
                                               mock_archive):
        archive = mock.Mock()
        mock_archive.return_value = archive

        exists_results = [True, False]

        def side_effect(arg):
            return exists_results.pop()

        mock_os_path.side_effect = side_effect

        self.setup.import_overlay_files()

        mock_archive.assert_called_once_with('description_dir/root.tar.gz')
        archive.extract.assert_called_once_with('root_dir')

    @patch('kiwi.system.setup.Command.run')
    def test_setup_hardware_clock(self, mock_command):
        self.setup.preferences['hwclock'] = 'clock'
        self.setup.setup_hardware_clock()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'hwclock', '--adjust', '--clock'])

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('os.path.exists')
    def test_setup_keyboard_map(self, mock_path, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"'])

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('os.path.exists')
    def test_setup_locale(self, mock_path, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig',
            ['root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8'])

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_locale_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone(self, mock_command):
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_called_once_with([
            'chroot', 'root_dir', 'ln', '-s', '-f',
            '/usr/share/zoneinfo/timezone', '/etc/localtime'
        ])

    @patch('kiwi.system.setup.Users')
    def test_setup_groups(self, mock_users):
        users = mock.Mock()
        users.group_exists = mock.Mock(return_value=False)
        mock_users.return_value = users

        self.setup_with_real_xml.setup_groups()

        calls = [call('users'), call('kiwi'), call('admin')]
        users.group_exists.assert_has_calls(calls)

        calls = [call('users', []), call('kiwi', []), call('admin', [])]
        users.group_add.assert_has_calls(calls)

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_add(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(return_value=False)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob', '-m', '-d', '/root'
            ]),
            call('tux', [
                '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux'
            ]),
            call('kiwi', [
                '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m',
                '-d', '/home/kiwi'
            ])
        ]
        users.user_add.assert_has_calls(calls)

        mock_command.assert_called_with(
            ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd'])

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(return_value=True)
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()
        calls = [call('root'), call('tux'), call('kiwi')]
        users.user_exists.assert_has_calls(calls)

        calls = [
            call('root', [
                '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c',
                'Bob'
            ]),
            call('tux', ['-p', 'password-hash', '-g', 'users']),
            call('kiwi',
                 ['-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users'])
        ]
        users.user_modify.assert_has_calls(calls)

    @patch_open
    @patch('os.path.exists')
    def test_import_image_identifier(self, mock_os_path, mock_open):
        self.xml_state.xml_data.get_id = mock.Mock(return_value='42')
        mock_os_path.return_value = True
        mock_open.return_value = self.context_manager_mock
        self.setup.import_image_identifier()
        mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w')
        self.file_mock.write.assert_called_once_with('42\n')

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_config_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple('result', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_config_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/config.sh'])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/images.sh'])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_config_script(self, mock_abspath, mock_exists,
                                          mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_config_script('ext4', 1)
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_config.sh')
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh ext4 1'
        ])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    @patch('os.path.abspath')
    def test_call_edit_boot_install_script(self, mock_abspath, mock_exists,
                                           mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_exists.return_value = True
        mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh'
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script('my_image.raw',
                                                 '/dev/mapper/loop0p1')
        mock_abspath.assert_called_once_with(
            'root_dir/image/edit_boot_install.sh')
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh my_image.raw /dev/mapper/loop0p1'
        ])

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script_raises(self, mock_os_path, mock_watch,
                                      mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script_raises(self, mock_os_path,
                                                  mock_watch, mock_command):
        result_type = namedtuple('result_type', ['stderr', 'returncode'])
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script('my_image.raw',
                                                 '/dev/mapper/loop0p1')

    @patch('kiwi.command.Command.run')
    def test_create_init_link_from_linuxrc(self, mock_command):
        self.setup.create_init_link_from_linuxrc()
        mock_command.assert_called_once_with(
            ['ln', 'root_dir/linuxrc', 'root_dir/init'])

    @patch('kiwi.command.Command.run')
    def test_create_recovery_archive_cleanup_only(self, mock_command):
        self.setup.oemconfig['recovery'] = False
        self.setup.create_recovery_archive()
        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])

    @patch_open
    def test_create_fstab(self, mock_open):
        mock_open.return_value = self.context_manager_mock
        self.setup.create_fstab(['fstab_entry'])
        mock_open.assert_called_once_with('root_dir/etc/fstab', 'w')
        self.file_mock.write.assert_called_once_with('fstab_entry\n')

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.NamedTemporaryFile')
    @patch('kiwi.system.setup.ArchiveTar')
    @patch_open
    @patch('kiwi.system.setup.Compress')
    @patch('os.path.getsize')
    @patch('kiwi.system.setup.Path.wipe')
    def test_create_recovery_archive(self, mock_wipe, mock_getsize,
                                     mock_compress, mock_open, mock_archive,
                                     mock_temp, mock_command):
        mock_open.return_value = self.context_manager_mock
        mock_getsize.return_value = 42
        compress = mock.Mock()
        mock_compress.return_value = compress
        archive = mock.Mock()
        mock_archive.return_value = archive
        tmpdir = mock.Mock()
        tmpdir.name = 'tmpdir'
        mock_temp.return_value = tmpdir
        self.setup.oemconfig['recovery'] = True
        self.setup.oemconfig['recovery_inplace'] = True

        self.setup.create_recovery_archive()

        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*'])
        mock_archive.assert_called_once_with(create_from_file_list=False,
                                             filename='tmpdir')
        archive.create.assert_called_once_with(exclude=['dev', 'proc', 'sys'],
                                               options=[
                                                   '--numeric-owner',
                                                   '--hard-dereference',
                                                   '--preserve-permissions'
                                               ],
                                               source_dir='root_dir')
        assert mock_command.call_args_list[1] == call(
            ['mv', 'tmpdir', 'root_dir/recovery.tar'])
        assert mock_open.call_args_list[0] == call(
            'root_dir/recovery.tar.filesystem', 'w')
        assert self.file_mock.write.call_args_list[0] == call('ext3')
        assert mock_command.call_args_list[2] == call(
            ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l'])
        assert mock_open.call_args_list[1] == call(
            'root_dir/recovery.tar.files', 'w')
        assert mock_getsize.call_args_list[0] == call('root_dir/recovery.tar')
        assert self.file_mock.write.call_args_list[1] == call('1\n')
        assert mock_open.call_args_list[2] == call(
            'root_dir/recovery.tar.size', 'w')
        assert self.file_mock.write.call_args_list[2] == call('42')
        mock_compress.assert_called_once_with('root_dir/recovery.tar')
        compress.gzip.assert_called_once_with()
        assert mock_getsize.call_args_list[1] == call(
            'root_dir/recovery.tar.gz')
        assert mock_open.call_args_list[3] == call(
            'root_dir/recovery.partition.size', 'w')
        assert self.file_mock.write.call_args_list[3] == call('300')
        mock_wipe.assert_called_once_with('root_dir/recovery.tar.gz')

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.create')
    @patch('os.path.exists')
    def test_export_modprobe_setup(self, mock_exists, mock_path, mock_command):
        mock_exists.return_value = True
        self.setup.export_modprobe_setup('target_root_dir')
        mock_path.assert_called_once_with('target_root_dir/etc')
        mock_command.assert_called_once_with([
            'rsync', '-z', '-a', 'root_dir/etc/modprobe.d',
            'target_root_dir/etc/'
        ])

    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    @patch_open
    def test_export_rpm_package_list(self, mock_open, mock_exists,
                                     mock_command):
        command = mock.Mock()
        command.output = 'packages_data'
        mock_exists.return_value = True
        mock_command.return_value = command
        result = self.setup.export_rpm_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}|\\n'
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w')

    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    @patch_open
    def test_export_rpm_package_verification(self, mock_open, mock_exists,
                                             mock_command):
        command = mock.Mock()
        command.output = 'verification_data'
        mock_exists.return_value = True
        mock_command.return_value = command
        result = self.setup.export_rpm_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(
            command=['rpm', '--root', 'root_dir', '-Va'], raise_on_error=False)
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w')

    @patch('kiwi.system.setup.Command.run')
    def test_set_selinux_file_contexts(self, mock_command):
        self.setup.set_selinux_file_contexts('security_context_file')
        mock_command.assert_called_once_with([
            'chroot', 'root_dir', 'setfiles', 'security_context_file', '/',
            '-v'
        ])

    @patch('kiwi.system.setup.Repository')
    def test_import_repositories_marked_as_imageinclude(self, mock_repo):
        repo = mock.Mock()
        mock_repo.return_value = repo
        self.setup_with_real_xml.import_repositories_marked_as_imageinclude()
        repo.add_repo.assert_called_once_with(
            '95811799a6d1889c5b2363d3886986de',
            'http://download.opensuse.org/repositories/Devel:PubCloud:AmazonEC2/SLE_12_GA',
            'rpm-md', None, None, None, None, None, 'kiwiRepoCredentials',
            None, None)
Ejemplo n.º 16
0
    def process(self):  # noqa: C901
        """
        Prepare and install a new system for chroot access
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        self.load_xml_description(self.command_args['--description'])

        abs_root_path = os.path.abspath(self.command_args['--root'])

        self.runtime_checker.check_efi_mode_for_disk_overlay_correctly_setup()
        self.runtime_checker.check_grub_efi_installed_for_efi_firmware()
        self.runtime_checker.check_boot_description_exists()
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_xen_uniquely_setup_as_server_or_guest()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_root_path)
        self.runtime_checker.check_mediacheck_only_for_x86_arch()
        self.runtime_checker.check_dracut_module_for_live_iso_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_overlay_in_package_list(
        )
        self.runtime_checker.check_dracut_module_for_disk_oem_in_package_list()
        self.runtime_checker.check_dracut_module_for_oem_install_in_package_list(
        )

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo']))

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(*self.sextuple_token(add_repo))

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag'])

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from'])

        self.runtime_checker.check_repositories_configured()
        self.runtime_checker.check_image_include_repos_publicly_resolvable()

        log.info('Preparing system')
        system = SystemPrepare(self.xml_state, abs_root_path,
                               self.command_args['--allow-existing-root'])
        manager = system.setup_repositories(self.command_args['--clear-cache'],
                                            self.command_args['--signing-key'])
        system.install_bootstrap(manager)
        system.install_system(manager)

        if self.command_args['--add-package']:
            system.install_packages(manager,
                                    self.command_args['--add-package'])
        if self.command_args['--delete-package']:
            system.delete_packages(manager,
                                   self.command_args['--delete-package'])

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(self.xml_state, abs_root_path)
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # make sure system instance is cleaned up now
        del system
Ejemplo n.º 17
0
class TestSystemSetup(object):
    @patch('platform.machine')
    def setup(self, mock_machine):
        mock_machine.return_value = 'x86_64'
        self.xml_state = mock.MagicMock()
        self.xml_state.build_type.get_filesystem = mock.Mock(
            return_value='ext3'
        )
        self.xml_state.xml_data.get_name = mock.Mock(
            return_value='some-image'
        )
        self.xml_state.get_image_version = mock.Mock(
            return_value='1.2.3'
        )
        self.xml_state.xml_data.description_dir = 'description_dir'
        self.setup = SystemSetup(
            self.xml_state, 'root_dir'
        )
        description = XMLDescription(
            description='../data/example_config.xml',
            derived_from='derived/description'
        )
        self.setup_with_real_xml = SystemSetup(
            XMLState(description.load()), 'root_dir'
        )
        command_run = namedtuple(
            'command', ['output', 'error', 'returncode']
        )
        self.run_result = command_run(
            output='password-hash\n',
            error='stderr',
            returncode=0
        )

    @patch('platform.machine')
    def test_setup_ix86(self, mock_machine):
        mock_machine.return_value = 'i686'
        setup = SystemSetup(
            mock.MagicMock(), 'root_dir'
        )
        assert setup.arch == 'ix86'

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description(self, mock_path, mock_open, mock_command):
        mock_path.return_value = True
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp', Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call(['cp', '../data/bootstrap.tgz', 'root_dir/image/'])]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    def test_import_description_archive_from_derived(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [
            True, False, True, True, True, True, True
        ]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

        assert mock_command.call_args_list == [
            call(['mkdir', '-p', 'root_dir/image']),
            call(['cp', '../data/config.sh', 'root_dir/image/config.sh']),
            call([
                'cp', '../data/my_edit_boot_script',
                'root_dir/image/edit_boot_config.sh'
            ]),
            call([
                'cp', '/absolute/path/to/my_edit_boot_install',
                'root_dir/image/edit_boot_install.sh'
            ]),
            call(['cp', '../data/images.sh', 'root_dir/image/images.sh']),
            call([
                'cp', Defaults.project_file('config/functions.sh'),
                'root_dir/.kconfig'
            ]),
            call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']),
            call([
                'cp', 'derived/description/bootstrap.tgz', 'root_dir/image/'
            ])
        ]

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_editboot_scripts_not_found(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [False, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    @patch_open
    @patch('os.path.exists')
    @raises(KiwiImportDescriptionError)
    def test_import_description_configured_archives_not_found(
        self, mock_path, mock_open, mock_command
    ):
        path_return_values = [False, False, True, True, True, True]

        def side_effect(arg):
            return path_return_values.pop()

        mock_path.side_effect = side_effect
        self.setup_with_real_xml.import_description()

    @patch('kiwi.command.Command.run')
    def test_cleanup(self, mock_command):
        self.setup.cleanup()
        mock_command.assert_called_once_with(
            ['rm', '-r', '-f', '/.kconfig', '/image']
        )

    @patch_open
    def test_import_shell_environment(self, mock_open):
        mock_profile = mock.MagicMock()
        mock_profile.create = mock.Mock(
            return_value=['a']
        )
        context_manager_mock = mock.Mock()
        mock_open.return_value = context_manager_mock
        file_mock = mock.Mock()
        enter_mock = mock.Mock()
        exit_mock = mock.Mock()
        enter_mock.return_value = file_mock
        setattr(context_manager_mock, '__enter__', enter_mock)
        setattr(context_manager_mock, '__exit__', exit_mock)

        self.setup.import_shell_environment(mock_profile)

        mock_profile.create.assert_called_once_with()
        mock_open.assert_called_once_with('root_dir/.profile', 'w')
        file_mock.write.assert_called_once_with('a\n')

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_overlay_files_copy_links(self, mock_os_path, mock_command):
        mock_os_path.return_value = True
        self.setup.import_overlay_files(
            follow_links=True, preserve_owner_group=True
        )
        mock_command.assert_called_once_with(
            [
                'rsync', '-r', '-p', '-t', '-D', '-H', '-X', '-A',
                '--one-file-system', '--copy-links', '-o', '-g',
                'description_dir/root/', 'root_dir'
            ]
        )

    @patch('kiwi.command.Command.run')
    @patch('os.path.exists')
    def test_import_overlay_files_links(self, mock_os_path, mock_command):
        mock_os_path.return_value = True
        self.setup.import_overlay_files(
            follow_links=False, preserve_owner_group=True
        )
        mock_command.assert_called_once_with(
            [
                'rsync', '-r', '-p', '-t', '-D', '-H', '-X', '-A',
                '--one-file-system', '--links', '-o', '-g',
                'description_dir/root/', 'root_dir'
            ]
        )

    @patch('kiwi.system.setup.ArchiveTar')
    @patch('os.path.exists')
    def test_import_overlay_files_from_archive(
        self, mock_os_path, mock_archive
    ):
        archive = mock.Mock()
        mock_archive.return_value = archive

        exists_results = [True, False]

        def side_effect(arg):
            return exists_results.pop()

        mock_os_path.side_effect = side_effect

        self.setup.import_overlay_files()

        mock_archive.assert_called_once_with(
            'description_dir/root.tar.gz'
        )
        archive.extract.assert_called_once_with(
            'root_dir'
        )

    @patch('kiwi.system.setup.Command.run')
    def test_setup_hardware_clock(self, mock_command):
        self.setup.preferences['hwclock'] = 'clock'
        self.setup.setup_hardware_clock()
        mock_command.assert_called_once_with(
            [
                'chroot', 'root_dir', 'hwclock', '--adjust', '--clock'
            ]
        )

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('os.path.exists')
    def test_setup_keyboard_map(self, mock_path, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig', [
                'root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"'
            ]
        )

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['keytable'] = 'keytable'
        self.setup.setup_keyboard_map()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Shell.run_common_function')
    @patch('os.path.exists')
    def test_setup_locale(self, mock_path, mock_shell):
        mock_path.return_value = True
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        mock_shell.assert_called_once_with(
            'baseUpdateSysConfig', [
                'root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8'
            ]
        )

    @patch('kiwi.logger.log.warning')
    @patch('os.path.exists')
    def test_setup_locale_skipped(self, mock_exists, mock_log_warn):
        mock_exists.return_value = False
        self.setup.preferences['locale'] = 'locale1,locale2'
        self.setup.setup_locale()
        assert mock_log_warn.called

    @patch('kiwi.system.setup.Command.run')
    def test_setup_timezone(self, mock_command):
        self.setup.preferences['timezone'] = 'timezone'
        self.setup.setup_timezone()
        mock_command.assert_called_once_with([
            'chroot', 'root_dir', 'ln', '-s', '-f',
            '/usr/share/zoneinfo/timezone', '/etc/localtime'
        ])

    @patch('kiwi.system.setup.Users')
    def test_setup_groups(self, mock_users):
        users = mock.Mock()
        users.group_exists = mock.Mock(
            return_value=False
        )
        mock_users.return_value = users

        self.setup_with_real_xml.setup_groups()

        users.group_exists.assert_called_once_with('root')
        users.group_add.assert_called_once_with('root', ['-g', 42])

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_add(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(
            return_value=False
        )
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        users.user_exists.assert_called_once_with('root')
        users.user_add.assert_called_once_with(
            'root', [
                '-p', 'password-hash',
                '-s', '/bin/bash', '-g', 42, '-u', 815, '-c', 'Bob',
                '-m', '-d', '/root'
            ]
        )
        mock_command.assert_called_once_with(
            ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd']
        )

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify(self, mock_command, mock_users):
        users = mock.Mock()
        users.user_exists = mock.Mock(
            return_value=True
        )
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        users.user_exists.assert_called_once_with('root')
        users.user_modify.assert_called_once_with(
            'root', [
                '-p', 'password-hash',
                '-s', '/bin/bash', '-g', 42, '-u', 815, '-c', 'Bob'
            ]
        )

    @patch('kiwi.system.setup.Users')
    @patch('kiwi.system.setup.Command.run')
    def test_setup_users_modify_group_name(self, mock_command, mock_users):
        # unset group id and expect use of group name now
        self.setup_with_real_xml.xml_state.xml_data.get_users()[0].set_id(None)
        users = mock.Mock()
        users.user_exists = mock.Mock(
            return_value=True
        )
        mock_users.return_value = users
        mock_command.return_value = self.run_result

        self.setup_with_real_xml.setup_users()

        users.user_modify.assert_called_once_with(
            'root', [
                '-p', 'password-hash',
                '-s', '/bin/bash', '-g', 'root', '-u', 815, '-c', 'Bob'
            ]
        )

    @patch_open
    @patch('os.path.exists')
    def test_import_image_identifier(self, mock_os_path, mock_open):
        self.xml_state.xml_data.get_id = mock.Mock(
            return_value='42'
        )
        mock_os_path.return_value = True
        context_manager_mock = mock.Mock()
        mock_open.return_value = context_manager_mock
        file_mock = mock.Mock()
        enter_mock = mock.Mock()
        exit_mock = mock.Mock()
        enter_mock.return_value = file_mock
        setattr(context_manager_mock, '__enter__', enter_mock)
        setattr(context_manager_mock, '__exit__', exit_mock)

        self.setup.import_image_identifier()

        mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w')
        file_mock.write.assert_called_once_with('42\n')

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_config_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple(
            'result', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_config_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/config.sh']
        )

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script(self, mock_os_path, mock_watch, mock_command):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()
        mock_command.assert_called_once_with(
            ['chroot', 'root_dir', 'bash', '/image/images.sh']
        )

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_config_script(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_config_script('ext4', 1)
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc image/edit_boot_config.sh ext4 1'
        ])

    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=0)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script(
            'my_image.raw', '/dev/mapper/loop0p1'
        )
        mock_command.assert_called_once_with([
            'bash', '-c',
            'cd root_dir && bash --norc image/edit_boot_install.sh my_image.raw /dev/mapper/loop0p1'
        ])

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_image_script_raises(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_image_script()

    @raises(KiwiScriptFailed)
    @patch('kiwi.command.Command.call')
    @patch('kiwi.command_process.CommandProcess.poll_and_watch')
    @patch('os.path.exists')
    def test_call_edit_boot_install_script_raises(
        self, mock_os_path, mock_watch, mock_command
    ):
        result_type = namedtuple(
            'result_type', ['stderr', 'returncode']
        )
        mock_result = result_type(stderr='stderr', returncode=1)
        mock_os_path.return_value = True
        mock_watch.return_value = mock_result
        self.setup.call_edit_boot_install_script(
            'my_image.raw', '/dev/mapper/loop0p1'
        )

    @patch('kiwi.command.Command.run')
    def test_create_init_link_from_linuxrc(self, mock_command):
        self.setup.create_init_link_from_linuxrc()
        mock_command.assert_called_once_with(
            ['ln', 'root_dir/linuxrc', 'root_dir/init']
        )

    @patch('kiwi.command.Command.run')
    def test_create_recovery_archive_cleanup_only(self, mock_command):
        self.setup.oemconfig['recovery'] = False
        self.setup.create_recovery_archive()
        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*']
        )

    @patch('kiwi.command.Command.run')
    @patch('kiwi.system.setup.NamedTemporaryFile')
    @patch('kiwi.system.setup.ArchiveTar')
    @patch_open
    @patch('kiwi.system.setup.Compress')
    @patch('os.path.getsize')
    @patch('kiwi.system.setup.Path.wipe')
    def test_create_recovery_archive(
        self, mock_wipe, mock_getsize, mock_compress,
        mock_open, mock_archive, mock_temp, mock_command
    ):
        context_manager_mock = mock.Mock()
        mock_open.return_value = context_manager_mock
        file_mock = mock.Mock()
        enter_mock = mock.Mock()
        exit_mock = mock.Mock()
        enter_mock.return_value = file_mock
        setattr(context_manager_mock, '__enter__', enter_mock)
        setattr(context_manager_mock, '__exit__', exit_mock)
        mock_getsize.return_value = 42
        compress = mock.Mock()
        mock_compress.return_value = compress
        archive = mock.Mock()
        mock_archive.return_value = archive
        tmpdir = mock.Mock()
        tmpdir.name = 'tmpdir'
        mock_temp.return_value = tmpdir
        self.setup.oemconfig['recovery'] = True
        self.setup.oemconfig['recovery_inplace'] = True

        self.setup.create_recovery_archive()

        assert mock_command.call_args_list[0] == call(
            ['bash', '-c', 'rm -f root_dir/recovery.*']
        )
        mock_archive.assert_called_once_with(
            create_from_file_list=False, filename='tmpdir'
        )
        archive.create.assert_called_once_with(
            exclude=['dev', 'proc', 'sys'],
            options=[
                '--numeric-owner',
                '--hard-dereference',
                '--preserve-permissions'
            ],
            source_dir='root_dir'
        )
        assert mock_command.call_args_list[1] == call(
            ['mv', 'tmpdir', 'root_dir/recovery.tar']
        )
        assert mock_open.call_args_list[0] == call(
            'root_dir/recovery.tar.filesystem', 'w'
        )
        assert file_mock.write.call_args_list[0] == call('ext3')
        assert mock_command.call_args_list[2] == call(
            ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l']
        )
        assert mock_open.call_args_list[1] == call(
            'root_dir/recovery.tar.files', 'w'
        )
        assert mock_getsize.call_args_list[0] == call(
            'root_dir/recovery.tar'
        )
        assert file_mock.write.call_args_list[1] == call('1\n')
        assert mock_open.call_args_list[2] == call(
            'root_dir/recovery.tar.size', 'w'
        )
        assert file_mock.write.call_args_list[2] == call('42')
        mock_compress.assert_called_once_with(
            'root_dir/recovery.tar'
        )
        compress.gzip.assert_called_once_with()
        assert mock_getsize.call_args_list[1] == call(
            'root_dir/recovery.tar.gz'
        )
        assert mock_open.call_args_list[3] == call(
            'root_dir/recovery.partition.size', 'w'
        )
        assert file_mock.write.call_args_list[3] == call('300')
        mock_wipe.assert_called_once_with(
            'root_dir/recovery.tar.gz'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch('kiwi.system.setup.Path.create')
    @patch('os.path.exists')
    def test_export_modprobe_setup(self, mock_exists, mock_path, mock_command):
        mock_exists.return_value = True
        self.setup.export_modprobe_setup('target_root_dir')
        mock_path.assert_called_once_with('target_root_dir/etc')
        mock_command.assert_called_once_with(
            [
                'rsync', '-z', '-a',
                'root_dir/etc/modprobe.d', 'target_root_dir/etc/'
            ]
        )

    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    @patch_open
    def test_export_rpm_package_list(
        self, mock_open, mock_exists, mock_command
    ):
        command = mock.Mock()
        command.output = 'packages_data'
        mock_exists.return_value = True
        mock_command.return_value = command
        result = self.setup.export_rpm_package_list('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
        mock_command.assert_called_once_with([
            'rpm', '--root', 'root_dir', '-qa', '--qf',
            '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}|\\n'
        ])
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.packages', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    @patch('os.path.exists')
    @patch_open
    def test_export_rpm_package_verification(
        self, mock_open, mock_exists, mock_command
    ):
        command = mock.Mock()
        command.output = 'verification_data'
        mock_exists.return_value = True
        mock_command.return_value = command
        result = self.setup.export_rpm_package_verification('target_dir')
        assert result == 'target_dir/some-image.x86_64-1.2.3.verified'
        mock_command.assert_called_once_with(
            command=['rpm', '--root', 'root_dir', '-Va'],
            raise_on_error=False
        )
        mock_open.assert_called_once_with(
            'target_dir/some-image.x86_64-1.2.3.verified', 'w'
        )

    @patch('kiwi.system.setup.Command.run')
    def test_set_selinux_file_contexts(self, mock_command):
        self.setup.set_selinux_file_contexts('security_context_file')
        mock_command.assert_called_once_with(
            [
                'chroot', 'root_dir',
                'setfiles', 'security_context_file', '/', '-v'
            ]
        )

    @patch('kiwi.system.setup.Repository')
    def test_import_repositories_marked_as_imageinclude(self, mock_repo):
        repo = mock.Mock()
        mock_repo.return_value = repo
        self.setup_with_real_xml.import_repositories_marked_as_imageinclude()
        repo.delete_all_repos.assert_called_once_with()
        repo.add_repo.assert_called_once_with(
            '95811799a6d1889c5b2363d3886986de',
            'http://download.opensuse.org/repositories/Devel:PubCloud:AmazonEC2/SLE_12_GA',
            'rpm-md',
            None,
            None,
            None
        )
Ejemplo n.º 18
0
class BootImageKiwi(BootImageBase):
    """
    **Implements preparation and creation of kiwi boot(initrd) images**

    The kiwi initrd is a customized first boot initrd which allows
    to control the first boot an appliance. The kiwi initrd replaces
    itself after first boot by the result of dracut.
    """
    def post_init(self):
        """
        Post initialization method

        Creates custom directory to prepare the boot image
        root filesystem which is a separate image to create
        the initrd from
        """
        self.boot_root_directory = mkdtemp(prefix='kiwi_boot_root.',
                                           dir=self.target_dir)
        self.temp_directories.append(self.boot_root_directory)

    def prepare(self):
        """
        Prepare new root system suitable to create a kiwi initrd from it
        """
        self.load_boot_xml_description()
        boot_image_name = self.boot_xml_state.xml_data.get_name()

        self.import_system_description_elements()

        log.info('Preparing boot image')
        system = SystemPrepare(xml_state=self.boot_xml_state,
                               root_dir=self.boot_root_directory,
                               allow_existing=True)
        manager = system.setup_repositories(signing_keys=self.signing_keys)
        system.install_bootstrap(manager)
        system.install_system(manager)

        profile = Profile(self.boot_xml_state)
        profile.add('kiwi_initrdname', boot_image_name)

        defaults = Defaults()
        defaults.to_profile(profile)

        self.setup = SystemSetup(self.boot_xml_state, self.boot_root_directory)
        profile.create(Defaults.get_profile_file(self.boot_root_directory))
        self.setup.import_description()
        self.setup.import_overlay_files(follow_links=True)
        self.setup.call_config_script()

        system.pinch_system(manager=manager, force=True)
        # make sure system instance is cleaned up before setting up
        del system

        self.setup.call_image_script()
        self.setup.create_init_link_from_linuxrc()

    def create_initrd(self, mbrid=None, basename=None, install_initrd=False):
        """
        Create initrd from prepared boot system tree and compress the result

        :param object mbrid: instance of ImageIdentifier
        :param string basename: base initrd file name
        :param bool install_initrd: installation media initrd

        """
        if self.is_prepared():
            log.info('Creating initrd cpio archive')
            # we can't simply exclude boot when building the archive
            # because the file boot/mbrid must be preserved. Because of
            # that we create a copy of the boot directory and remove
            # everything in boot/ except for boot/mbrid. The original
            # boot directory should not be changed because we rely
            # on other data in boot/ e.g the kernel to be available
            # for the entire image building process
            if basename:
                kiwi_initrd_basename = basename
            else:
                kiwi_initrd_basename = self.initrd_base_name
            temp_boot_root_directory = mkdtemp(prefix='kiwi_boot_root_copy.')
            self.temp_directories.append(temp_boot_root_directory)
            data = DataSync(self.boot_root_directory + '/',
                            temp_boot_root_directory)
            data.sync_data(options=['-a'])
            boot_directory = temp_boot_root_directory + '/boot'
            Path.wipe(boot_directory)
            if mbrid:
                log.info('--> Importing mbrid: %s', mbrid.get_id())
                Path.create(boot_directory)
                image_identifier = boot_directory + '/mbrid'
                mbrid.write(image_identifier)

            cpio = ArchiveCpio(
                os.sep.join([self.target_dir, kiwi_initrd_basename]))
            # the following is a list of directories which were needed
            # during the process of creating an image but not when the
            # image is actually booting with this initrd
            exclude_from_archive = [
                '/' + Defaults.get_shared_cache_location(), '/image',
                '/usr/lib/grub*'
            ]
            # the following is a list of directories to exclude which
            # are not needed inside of the initrd
            exclude_from_archive += [
                '/usr/share/doc', '/usr/share/man', '/home', '/media', '/srv'
            ]
            cpio.create(source_dir=temp_boot_root_directory,
                        exclude=exclude_from_archive)
            log.info('--> xz compressing archive')
            compress = Compress(
                os.sep.join([self.target_dir, kiwi_initrd_basename]))
            compress.xz(['--check=crc32', '--lzma2=dict=1MiB', '--threads=0'])
            self.initrd_filename = compress.compressed_filename
Ejemplo n.º 19
0
    def process(self):                                      # noqa: C901
        """
        Build a system image from the specified description. The
        build command combines the prepare and create commands
        """
        self.manual = Help()
        if self._help():
            return

        Privileges.check_for_root_permissions()

        abs_target_dir_path = os.path.abspath(
            self.command_args['--target-dir']
        )
        build_dir = os.sep.join([abs_target_dir_path, 'build'])
        image_root = os.sep.join([build_dir, 'image-root'])
        Path.create(build_dir)

        if not self.global_args['--logfile']:
            log.set_logfile(
                os.sep.join([abs_target_dir_path, 'build', 'image-root.log'])
            )

        self.load_xml_description(
            self.command_args['--description']
        )
        self.runtime_checker.check_minimal_required_preferences()
        self.runtime_checker.check_efi_mode_for_disk_overlay_correctly_setup()
        self.runtime_checker.check_boot_description_exists()
        self.runtime_checker.check_consistent_kernel_in_boot_and_system_image()
        self.runtime_checker.check_docker_tool_chain_installed()
        self.runtime_checker.check_volume_setup_defines_multiple_fullsize_volumes()
        self.runtime_checker.check_volume_setup_has_no_root_definition()
        self.runtime_checker.check_volume_label_used_with_lvm()
        self.runtime_checker.check_xen_uniquely_setup_as_server_or_guest()
        self.runtime_checker.check_target_directory_not_in_shared_cache(
            abs_target_dir_path
        )
        self.runtime_checker.check_mediacheck_only_for_x86_arch()
        self.runtime_checker.check_dracut_module_for_live_iso_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_overlay_in_package_list()
        self.runtime_checker.check_dracut_module_for_disk_oem_in_package_list()
        self.runtime_checker.check_dracut_module_for_oem_install_in_package_list()

        if self.command_args['--ignore-repos']:
            self.xml_state.delete_repository_sections()
        elif self.command_args['--ignore-repos-used-for-build']:
            self.xml_state.delete_repository_sections_used_for_build()

        if self.command_args['--set-repo']:
            self.xml_state.set_repository(
                *self.sextuple_token(self.command_args['--set-repo'])
            )

        if self.command_args['--add-repo']:
            for add_repo in self.command_args['--add-repo']:
                self.xml_state.add_repository(
                    *self.sextuple_token(add_repo)
                )

        if self.command_args['--set-container-tag']:
            self.xml_state.set_container_config_tag(
                self.command_args['--set-container-tag']
            )

        if self.command_args['--add-container-label']:
            for add_label in self.command_args['--add-container-label']:
                try:
                    (name, value) = add_label.split('=', 1)
                    self.xml_state.add_container_config_label(name, value)
                except Exception:
                    log.warning(
                        'Container label {0} ignored. Invalid format: '
                        'expected labelname=value'.format(add_label)
                    )

        if self.command_args['--set-container-derived-from']:
            self.xml_state.set_derived_from_image_uri(
                self.command_args['--set-container-derived-from']
            )

        self.runtime_checker.check_repositories_configured()
        self.runtime_checker.check_image_include_repos_publicly_resolvable()

        log.info('Preparing new root system')
        system = SystemPrepare(
            self.xml_state,
            image_root,
            self.command_args['--allow-existing-root']
        )
        manager = system.setup_repositories(
            self.command_args['--clear-cache'],
            self.command_args['--signing-key']
        )
        system.install_bootstrap(manager)
        system.install_system(
            manager
        )
        if self.command_args['--add-package']:
            system.install_packages(
                manager, self.command_args['--add-package']
            )
        if self.command_args['--delete-package']:
            system.delete_packages(
                manager, self.command_args['--delete-package']
            )

        profile = Profile(self.xml_state)

        defaults = Defaults()
        defaults.to_profile(profile)

        setup = SystemSetup(
            self.xml_state, image_root
        )
        setup.import_shell_environment(profile)

        setup.import_description()
        setup.import_overlay_files()
        setup.import_image_identifier()
        setup.setup_groups()
        setup.setup_users()
        setup.setup_keyboard_map()
        setup.setup_locale()
        setup.setup_plymouth_splash()
        setup.setup_timezone()
        setup.setup_permissions()

        # make sure manager instance is cleaned up now
        del manager

        # setup permanent image repositories after cleanup
        setup.import_repositories_marked_as_imageinclude()
        setup.call_config_script()

        # handle uninstall package requests, gracefully uninstall
        # with dependency cleanup
        system.pinch_system(force=False)

        # handle delete package requests, forced uninstall without
        # any dependency resolution
        system.pinch_system(force=True)

        # delete any custom rpm macros created
        Rpm(
            image_root, Defaults.get_custom_rpm_image_macro_name()
        ).wipe_config()

        # make sure system instance is cleaned up now
        del system

        setup.call_image_script()

        # make sure setup instance is cleaned up now
        del setup

        log.info('Creating system image')
        image_builder = ImageBuilder(
            self.xml_state,
            abs_target_dir_path,
            image_root,
            custom_args={
                'signing_keys': self.command_args['--signing-key'],
                'xz_options': self.runtime_config.get_xz_options()
            }
        )
        result = image_builder.create()
        result.print_results()
        result.dump(
            os.sep.join([abs_target_dir_path, 'kiwi.result'])
        )