diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc index c6a9062b96f..e029c613ee9 100644 --- a/kiwi/schema/kiwi.rnc +++ b/kiwi/schema/kiwi.rnc @@ -3702,7 +3702,7 @@ div { attribute type { "bootstrap" | "delete" | "docker" | "image" | "iso" | "oem" | "pxe" | "kis" | "oci" | - "uninstall" + "uninstall" | "systemfiles" } k.packages.profiles.attribute = k.profiles.attribute k.packages.patternType.attribute = diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng index 4ea5fe26277..409d4dded60 100644 --- a/kiwi/schema/kiwi.rng +++ b/kiwi/schema/kiwi.rng @@ -5587,6 +5587,7 @@ packages are only installed if this build type is requested. kis oci uninstall + systemfiles diff --git a/kiwi/system/root_import/base.py b/kiwi/system/root_import/base.py index 738a4996105..ccd4ac708f7 100644 --- a/kiwi/system/root_import/base.py +++ b/kiwi/system/root_import/base.py @@ -123,6 +123,9 @@ def overlay_finalize(self, xml_state: XMLState) -> None: with open(systemfiles, 'a') as systemfiles_fd: # copy on write makes this file to become part of the delta systemfiles_fd.write(os.linesep) + with open(f'{systemfiles}.libs', 'a') as systemlibs_fd: + # copy on write makes this file to become part of the delta + systemlibs_fd.write(os.linesep) # Umount and rename upper to be the new root self.overlay.umount() diff --git a/kiwi/system/setup.py b/kiwi/system/setup.py index dc3fac541d5..5f36cfc5da3 100644 --- a/kiwi/system/setup.py +++ b/kiwi/system/setup.py @@ -625,7 +625,9 @@ def export_modprobe_setup(self, target_root_dir: str) -> None: options=['-a'] ) - def export_package_file_list(self, target_dir: str, file_name: str) -> str: + def export_flake_pilot_system_file_list( + self, target_dir: str, file_name: str + ) -> str: """ Export image package file list to the target_dir and filename @@ -638,7 +640,7 @@ def export_package_file_list(self, target_dir: str, file_name: str) -> str: ) result_file = os.path.normpath(f'{target_dir}/{file_name}') if packager == 'rpm': - self._export_rpm_file_list(result_file) + self._export_rpm_flake_pilot_system_file_list(result_file) return result_file return '' @@ -829,10 +831,10 @@ def call_edit_boot_install_script( def create_system_files(self) -> None: """ - Create file list of packages + Create file list of packages to be used by flake-pilot """ if self.xml_state.build_type.get_provide_system_files(): - self.export_package_file_list( + self.export_flake_pilot_system_file_list( self.root_dir, Defaults.get_system_files_name() ) @@ -1346,22 +1348,85 @@ def _text(self, section_content): if section_content: return section_content[0] - def _export_rpm_file_list(self, filename): - log.info('Export rpm packages file list') + def _export_rpm_flake_pilot_system_file_list(self, filename): + log.info('Export rpm packages system file list for flake-pilot') dbpath_option = [ '--dbpath', self._get_rpm_database_location() ] + skip_list = self.xml_state.get_system_files_ignore_packages() query_call = Command.run( - [ - 'rpm', '--root', self.root_dir, - '--noartifact', '--noghost', '-qal' - ] + dbpath_option + ['rpm', '--root', self.root_dir, '-qa'] + dbpath_option ) - with open(filename, 'w', encoding='utf-8') as packagefiles: - packagefiles.write( - os.linesep.join(sorted(query_call.output.splitlines())) - ) - packagefiles.write(os.linesep) + libcheck = [ + '/lib', '/usr/lib', '/lib64', '/usr/lib64' + ] + packmeta = {} + for package in query_call.output.splitlines(): + skip = False + for skip_package in skip_list: + if package.startswith(skip_package): + skip = True + break + if not skip and package: + package = package.strip() + packmeta[package] = {} + query_call = Command.run( + [ + 'rpm', '--root', self.root_dir, '-ql', + '--noghost', '--dump', package + ] + dbpath_option + ) + for raw in query_call.output.splitlines(): + meta = raw.lstrip().split(' ') + if len(meta) == 11: + islib = False + for libpath in libcheck: + if meta[0].startswith(libpath): + islib = True + break + packmeta[package][meta[0]] = { + 'islib': islib, + 'isdoc': True if meta[8] == '1' else False, + 'islink': True if meta[10] != 'X' else False, + 'linkname': meta[10] + } + + with open(filename, 'w', encoding='utf-8') as systemfiles: + for package in packmeta.keys(): + for file in packmeta[package].keys(): + meta = packmeta[package][file] + if not meta['isdoc'] and not meta['islib']: + systemfiles.write(f'{file}{os.linesep}') + + with open(f'{filename}.libs', 'w', encoding='utf-8') as systemlibs: + for package in packmeta.keys(): + for file in packmeta[package].keys(): + meta = packmeta[package][file] + if meta['islib']: + if meta['islink']: + systemlibs.write(f'{file}{os.linesep}') + elif not self._has_link_target(packmeta, file): + systemlibs.write(f'{file}{os.linesep}') + + def _has_link_target( + self, packages: Dict[str, Dict[str, Dict[str, str]]], filename: str + ) -> bool: + if 'ld-linux' in filename: + # exceptional case. /lib.../ld-linux-* could be a + # symlink to /usr/bin/ld.so but only providing + # /usr/bin/ld.so is not enough to call programs. + # Thus in this case we indicate the file has no link + # target such that the later pilot sync takes it + # into account. + return False + for package in packages.keys(): + for file in packages[package].keys(): + meta = packages[package][file] + base_filename = os.path.basename(filename) + base_linkname = os.path.basename(meta['linkname']) + if meta['islink'] and base_filename == base_linkname: + return True + return False def _export_rpm_package_list(self, filename): log.info('Export rpm packages metadata') diff --git a/kiwi/xml_state.py b/kiwi/xml_state.py index 36454e11b0e..9dca9754ddf 100644 --- a/kiwi/xml_state.py +++ b/kiwi/xml_state.py @@ -732,6 +732,17 @@ def get_ignore_packages(self, section_type: str) -> List: result.append(package.get_name().strip()) return sorted(result) + def get_system_files_ignore_packages(self) -> List[str]: + """ + List of ignore package names from the type="systemfiles" + packages section(s) + + :return: package names + + :rtype: list + """ + return self.get_ignore_packages('systemfiles') + def get_system_ignore_packages(self) -> List: """ List of ignore package names from the packages sections matching diff --git a/test/data/example_config.xml b/test/data/example_config.xml index 79b2a888b9f..3cc92053dbc 100644 --- a/test/data/example_config.xml +++ b/test/data/example_config.xml @@ -260,4 +260,9 @@ + + + + + diff --git a/test/data/rpm_ql_dump_glibc b/test/data/rpm_ql_dump_glibc new file mode 100644 index 00000000000..5c86eccd0cd --- /dev/null +++ b/test/data/rpm_ql_dump_glibc @@ -0,0 +1,48 @@ +/etc/bindresvport.blacklist 415 1733214801 4b0166e286cb27b577940432c6e39614b143b0d2c207dd3533906a19956e2c04 0100644 root root 1 0 0 X +/etc/gai.conf 0 1733214826 0000000000000000000000000000000000000000000000000000000000000000 0100644 root root 1 0 0 X +/etc/ld.so.cache 0 1733214801 0000000000000000000000000000000000000000000000000000000000000000 0100644 root root 1 0 0 X +/etc/ld.so.conf 206 1733214801 0ad7a03c5985fc18c3ee981325a60a7c3f8a169151b9cd653ddfe2e77f01fa26 0100644 root root 1 0 0 X +/etc/nsswitch.conf 2190 1733214801 6e084899135cda5df149d95e3dc79f22d1b4367b7c3b2fd74582d02be3c785cf 0100644 root root 1 0 0 X +/etc/rpc 1634 1733214698 3b24a975dcde688434258566813a83ce256a4c73efd7a8a9c3998327b0b4de68 0100644 root root 1 0 0 X +/lib64/ld-linux-x86-64.so.2 243504 1733214804 c70459f7af50cbae25fe31df8344062ee83b2963675e32ee103fd10a8a7be5df 0100755 root root 0 0 0 X +/lib64/ld-lsb-x86-64.so.3 20 1733214811 0000000000000000000000000000000000000000000000000000000000000000 0120777 root root 0 0 0 ld-linux-x86-64.so.2 +/lib64/libBrokenLocale.so.1 7560 1733214804 ba61bdf8b15c60bc81a2fa080f22fb39126ace4029d7f6e6d3a483ff91a41129 0100755 root root 0 0 0 X +/lib64/libanl.so.1 7384 1733214804 d53507cacee2da3cdc82b9f4736f2acdd8b39d756f65a12bddc6fb8707d038b3 0100755 root root 0 0 0 X +/lib64/libc.so.6 2449760 1733214804 d51f437bdf319de3d2c37443316c98541987603308a834a5511330c6bb1c91b8 0100755 root root 0 0 0 X +/lib64/libc_malloc_debug.so.0 66640 1733214804 76901cfa755ad9fe4669982f311b1b417f720ce8984253a668e496d4b9400b49 0100755 root root 0 0 0 X +/lib64/libdl.so.2 7552 1733214804 1f2534a7db24829fdc8b32b6a16938b5a52efde17f1e175ded9a8cab2bde1b2c 0100755 root root 0 0 0 X +/lib64/libm.so.6 1037488 1733214804 f1e5e586ee981e8efec0eb149126118a3e4644502f939831ff1daaf474beb44c 0100755 root root 0 0 0 X +/lib64/libmvec.so.1 1076328 1733214804 730556bf17e863368aab990007f8b505563712ea2323a866779302c9bb92745f 0100755 root root 0 0 0 X +/lib64/libnss_compat.so.2 45672 1733214804 fe63514a1f16aef4587c81808310b2fc7d1a2c2180938839b3a5744e76276d4b 0100755 root root 0 0 0 X +/lib64/libnss_db.so.2 40864 1733214804 8c848c3462df5ab90a8d3d6e5576449949e5ad55631423b546c9f3097e0030d2 0100755 root root 0 0 0 X +/lib64/libnss_dns.so.2 7056 1733214804 6e1e6a5a863b549240168c3cf6542fd798d30a8c81c9e9fbb3baa7d82d809b4c 0100755 root root 0 0 0 X +/lib64/libnss_files.so.2 7056 1733214804 e4c4621713e92c81673c80850b041828a1aa6b9b6dcd6b1ef8cf20d95e63a743 0100755 root root 0 0 0 X +/lib64/libnss_hesiod.so.2 26680 1733214804 d8984863043b1a087d66bfd3f7a8f6a9d73b0df4144942b5cac4a0a4bbcd1af6 0100755 root root 0 0 0 X +/lib64/libpthread.so.0 8408 1733214804 4f9d2dca66e07c9206190f9b6dde9ddc6eb3de5883865f5a0f365590d3cb528b 0100755 root root 0 0 0 X +/lib64/libresolv.so.2 73640 1733214804 d9c7d53ab627c3f116e2520c6bd29fa4403295ba02af73d95430e38fb9ba1876 0100755 root root 0 0 0 X +/lib64/librt.so.1 8176 1733214804 84bf7cec9a66333c1d33a93bd9d8f2d7211e25c8a4f328aa2739983c5b84da0b 0100755 root root 0 0 0 X +/lib64/libthread_db.so.1 49216 1733214804 5a0a2cf166b6207d905fc6f3e4d5dad6774d9bc76be81a58e28a6ef6ad51971a 0100755 root root 0 0 0 X +/lib64/libutil.so.1 7392 1733214804 ab7afc07bdc5be89ff703dc808b43c6dbc3c4885848b9463d5c5b7d243fac8a3 0100755 root root 0 0 0 X +/sbin/ldconfig 1073856 1733214804 86143be7fb0e7619d9eb8153591491f2e3ab821a9cdb44483954eb6fdf443f23 0100755 root root 0 0 0 X +/usr/bin/gencat 27120 1733214804 9754597fd4521fd9b2392fb91fbaea756d81a6bd9fe4d39929db38f9719c5aef 0100755 root root 0 0 0 X +/usr/bin/getconf 22 1733214811 0000000000000000000000000000000000000000000000000000000000000000 0120777 root root 0 0 0 ../lib/getconf/getconf +/usr/bin/getent 31616 1733214804 715f8eaf7d145853008a8169a4fb42ffa5a182288c609b827d9d2e9d5ca14872 0100755 root root 0 0 0 X +/usr/bin/iconv 64432 1733214804 a645cdd3ce6fa2c10ee097db9866cc199d8f3d15095c312a8a170b7acd21c9ef 0100755 root root 0 0 0 X +/usr/bin/ld.so 27 1733214811 0000000000000000000000000000000000000000000000000000000000000000 0120777 root root 0 0 0 /lib64/ld-linux-x86-64.so.2 +/usr/bin/ldd 5372 1733214700 d89312068d0940fc9ae75eac6f81ff60ca6750318567c4dd0caa646d966f6cd9 0100755 root root 0 0 0 X +/usr/bin/locale 51112 1733214804 9e1cc2506dc7aaa94db572320331481f92be2c6dad78a7cfc46b7988e2c6d847 0100755 root root 0 0 0 X +/usr/bin/localedef 336352 1733214804 fcee441a70cb59c6c709838582f6fc2ff015dfa4f4350d8a1f117847a9f8502e 0100755 root root 0 0 0 X +/usr/lib/getconf 0 1733214804 0000000000000000000000000000000000000000000000000000000000000000 040755 root root 0 0 0 X +/usr/lib/getconf/POSIX_V6_LP64_OFF64 31064 1733214804 347bece701b32a42b6cc545e9f63640765cfb493b9cc1c68507483a53c2db984 0100755 root root 0 0 0 X +/usr/lib/getconf/POSIX_V7_LP64_OFF64 31064 1733214804 347bece701b32a42b6cc545e9f63640765cfb493b9cc1c68507483a53c2db984 0100755 root root 0 0 0 X +/usr/lib/getconf/XBS5_LP64_OFF64 31064 1733214804 347bece701b32a42b6cc545e9f63640765cfb493b9cc1c68507483a53c2db984 0100755 root root 0 0 0 X +/usr/lib/getconf/getconf 31064 1733214804 347bece701b32a42b6cc545e9f63640765cfb493b9cc1c68507483a53c2db984 0100755 root root 0 0 0 X +/usr/sbin/iconvconfig 31376 1733214805 4cde0a5234ffd3caf7907f35a5e4b3de08eab17115bedd062cca4d19c17d2464 0100755 root root 0 0 0 X +/usr/share/doc/packages/glibc 0 1733214826 0000000000000000000000000000000000000000000000000000000000000000 040755 root root 0 0 0 X +/usr/share/doc/packages/glibc/gai.conf 2584 1690826056 76a5771adee7b9f36c7ae66eae78d72f325557500269107f2d98a7e3560a1808 0100644 root root 0 1 0 X +/usr/share/licenses/glibc 0 1733214826 0000000000000000000000000000000000000000000000000000000000000000 040755 root root 0 0 0 X +/usr/share/licenses/glibc/LICENSES 18943 1690826056 b33d0bd9f685b46853548814893a6135e74430d12f6d94ab3eba42fc591f83bc 0100644 root root 0 0 0 X +/usr/share/man/man1/gencat.1.gz 2619 1733214801 2aa75ddeb08e1772018ed74fe8bc1f1c4467c49c0a939052a2fe6ce2211f07da 0100644 root root 0 1 0 X +/usr/share/man/man1/getconf.1.gz 2346 1733214801 ec3d91ae40cbbf7aafb047a4799c25e00a04d340c0febe83d72f66ca2dfc7dc0 0100644 root root 0 1 0 X +/usr/share/man/man5/locale.alias.5.gz 949 1733214801 397f8cd51dc51c12e1b387201c40191e72bc5b9a92ffa77a0864505e5bc0ec86 0100644 root root 0 1 0 X +/var/cache/ldconfig 0 1733214801 0000000000000000000000000000000000000000000000000000000000000000 040700 root root 0 0 0 X diff --git a/test/unit/system/root_import/base_test.py b/test/unit/system/root_import/base_test.py index 6a51154a9e8..437474c8094 100644 --- a/test/unit/system/root_import/base_test.py +++ b/test/unit/system/root_import/base_test.py @@ -132,6 +132,7 @@ def test_overlay_finalize( ] # create removed files metadata for later host provisioning assert file_handle.write.call_args_list == [ + call('\n'), call('\n'), call('/file_a'), call(os.linesep), call('/file_b'), call(os.linesep) diff --git a/test/unit/system/setup_test.py b/test/unit/system/setup_test.py index e9db0949735..0b6a7f43da2 100644 --- a/test/unit/system/setup_test.py +++ b/test/unit/system/setup_test.py @@ -1286,10 +1286,10 @@ def test_export_package_list_unknown_packager( assert self.setup.export_package_list('target_dir') == '' @patch('kiwi.defaults.Defaults.get_default_packager_tool') - def test_export_package_file_list_unknown_packager( + def test_export_flake_pilot_system_file_list_unknown_packager( self, mock_get_default_packager_tool ): - assert self.setup.export_package_file_list( + assert self.setup.export_flake_pilot_system_file_list( 'target_dir', 'system_files' ) == '' @@ -1308,34 +1308,98 @@ def test_export_package_verification_unknown_packager( @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') - def test_export_package_file_list( - self, mock_MountManager, mock_RpmDataBase, mock_command + def test_export_flake_pilot_system_file_list( + self, mock_MountManager, mock_RpmDataBase, mock_command_run ): 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_file_data' - mock_command.return_value = command + with open('../data/rpm_ql_dump_glibc', 'r') as glibc: + rpm_ql_dump_glibc = glibc.read() + self.xml_state.get_system_files_ignore_packages = Mock( + return_value=['rpm', 'yast', 'zypp'] + ) - with patch('builtins.open') as m_open: - result = self.setup.export_package_file_list( + def command_call(args): + if args[3] == '-qa': + return Mock(output='glibc\nzypper\n') + else: + return Mock(output=rpm_ql_dump_glibc) + + mock_command_run.side_effect = command_call + + with patch('builtins.open') as mock_open: + mock_open.return_value = MagicMock(spec=io.IOBase) + file_handle = mock_open.return_value.__enter__.return_value + result = self.setup.export_flake_pilot_system_file_list( 'target_dir', 'system_files' ) - m_open.assert_called_once_with( - 'target_dir/system_files', 'w', encoding='utf-8' - ) - - assert result == 'target_dir/system_files' - mock_command.assert_called_once_with( - [ - 'rpm', '--root', 'root_dir', - '--noartifact', '--noghost', '-qal', - '--dbpath', 'image_dbpath' + assert mock_open.call_args_list == [ + call('target_dir/system_files', 'w', encoding='utf-8'), + call('target_dir/system_files.libs', 'w', encoding='utf-8') ] - ) + assert file_handle.write.call_args_list == [ + call('/etc/bindresvport.blacklist\n'), + call('/etc/gai.conf\n'), + call('/etc/ld.so.cache\n'), + call('/etc/ld.so.conf\n'), + call('/etc/nsswitch.conf\n'), + call('/etc/rpc\n'), + call('/sbin/ldconfig\n'), + call('/usr/bin/gencat\n'), + call('/usr/bin/getconf\n'), + call('/usr/bin/getent\n'), + call('/usr/bin/iconv\n'), + call('/usr/bin/ld.so\n'), + call('/usr/bin/ldd\n'), + call('/usr/bin/locale\n'), + call('/usr/bin/localedef\n'), + call('/usr/sbin/iconvconfig\n'), + call('/usr/share/doc/packages/glibc\n'), + call('/usr/share/licenses/glibc\n'), + call('/usr/share/licenses/glibc/LICENSES\n'), + call('/var/cache/ldconfig\n'), + call('/lib64/ld-linux-x86-64.so.2\n'), + call('/lib64/ld-lsb-x86-64.so.3\n'), + call('/lib64/libBrokenLocale.so.1\n'), + call('/lib64/libanl.so.1\n'), + call('/lib64/libc.so.6\n'), + call('/lib64/libc_malloc_debug.so.0\n'), + call('/lib64/libdl.so.2\n'), + call('/lib64/libm.so.6\n'), + call('/lib64/libmvec.so.1\n'), + call('/lib64/libnss_compat.so.2\n'), + call('/lib64/libnss_db.so.2\n'), + call('/lib64/libnss_dns.so.2\n'), + call('/lib64/libnss_files.so.2\n'), + call('/lib64/libnss_hesiod.so.2\n'), + call('/lib64/libpthread.so.0\n'), + call('/lib64/libresolv.so.2\n'), + call('/lib64/librt.so.1\n'), + call('/lib64/libthread_db.so.1\n'), + call('/lib64/libutil.so.1\n'), + call('/usr/lib/getconf/POSIX_V6_LP64_OFF64\n'), + call('/usr/lib/getconf/POSIX_V7_LP64_OFF64\n'), + call('/usr/lib/getconf/XBS5_LP64_OFF64\n') + ] + assert result == 'target_dir/system_files' + assert mock_command_run.call_args_list == [ + call( + [ + 'rpm', '--root', 'root_dir', + '-qa', '--dbpath', 'image_dbpath' + ] + ), + call( + [ + 'rpm', '--root', 'root_dir', + '-ql', '--noghost', '--dump', 'glibc', + '--dbpath', 'image_dbpath' + ] + ) + ] @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @@ -1435,10 +1499,12 @@ def test_setup_permissions( with self._caplog.at_level(logging.WARNING): self.setup.setup_permissions() - @patch.object(SystemSetup, 'export_package_file_list') - def test_create_system_files(self, mock_export_package_file_list): + @patch.object(SystemSetup, 'export_flake_pilot_system_file_list') + def test_create_system_files( + self, mock_export_flake_pilot_system_file_list + ): self.setup.create_system_files() - mock_export_package_file_list.assert_called_once_with( + mock_export_flake_pilot_system_file_list.assert_called_once_with( 'root_dir', 'systemfiles' ) diff --git a/test/unit/xml_state_test.py b/test/unit/xml_state_test.py index e74fe27f3eb..8ef832a8c56 100644 --- a/test/unit/xml_state_test.py +++ b/test/unit/xml_state_test.py @@ -310,6 +310,11 @@ def test_get_to_become_deleted_packages(self): 'kernel-debug' ] + def test_get_system_files_ignore_packages(self): + assert self.state.get_system_files_ignore_packages() == [ + 'rpm', 'yast', 'zypp' + ] + def test_get_build_type_vagrant_config_section(self): vagrant_config = self.state.get_build_type_vagrant_config_section() assert vagrant_config.get_provider() == 'libvirt'