From 3a066551c97891dde6a050f8ef2b7b188d94b9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Fri, 26 Oct 2018 15:17:59 +0200 Subject: [PATCH] Warn on Modifications to intermediate config files Some files are taken from the host and managed as intermediate config files during the build of the image. Changes to those files during the build run by e.g a script will not become effective because the file gets restored. With this patch we detect the modification condition and throw a warning message such that the author of the image can adapt the description as suggested in the message. This Fixes #817 --- kiwi/system/root_bind.py | 35 +++++++++++++++++++++++++- test/unit/system_root_bind_test.py | 40 ++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/kiwi/system/root_bind.py b/kiwi/system/root_bind.py index 1883041e..0b3b4f85 100644 --- a/kiwi/system/root_bind.py +++ b/kiwi/system/root_bind.py @@ -17,6 +17,7 @@ # import os import shutil +from textwrap import dedent # project from kiwi.command import Command @@ -24,6 +25,7 @@ from kiwi.defaults import Defaults from kiwi.logger import log from kiwi.path import Path from kiwi.mount_manager import MountManager +from kiwi.utils.checksum import Checksum from kiwi.exceptions import ( KiwiMountKernelFileSystemsError, @@ -150,6 +152,9 @@ class RootBind(object): Command.run( ['ln', '-s', '-f', link_target, self.root_dir + config] ) + checksum = Checksum(config) + with open(self.root_dir + config + '.sha', 'w') as shasum: + shasum.write(checksum.sha256()) except Exception as e: self.cleanup() raise KiwiSetupIntermediateConfigError( @@ -189,7 +194,35 @@ class RootBind(object): config_files_to_delete = [] for config in self.cleanup_files: - config_files_to_delete.append(self.root_dir + config) + config_file = self.root_dir + config + shasum_file = config_file.replace('.kiwi', '.sha') + + config_files_to_delete.append(config_file) + config_files_to_delete.append(shasum_file) + + checksum = Checksum(config_file) + if not checksum.matches(checksum.sha256(), shasum_file): + message = dedent('''\n + Modifications to intermediate config file detected + + The file: {0} + + is a copy from the host system and symlinked + to its origin in the image root during build time. + Modifications to this file by e.g script code will not + have any effect because the file gets restored by one + of the following actions: + + 1. A package during installation provides it + 2. A custom version of the file is setup as overlay file + 3. The file is not provided by install or overlay and will + be deleted at the end of the build process + + If you need a custom version of that file please + provide it as an overlay file in your image + description + ''') + log.warning(message.format(config_file)) del self.cleanup_files[:] diff --git a/test/unit/system_root_bind_test.py b/test/unit/system_root_bind_test.py index a00ba2f0..a1997283 100644 --- a/test/unit/system_root_bind_test.py +++ b/test/unit/system_root_bind_test.py @@ -2,7 +2,10 @@ from mock import patch from mock import call import mock -from .test_helper import raises +from .test_helper import ( + raises, + patch_open +) from kiwi.exceptions import ( KiwiMountKernelFileSystemsError, @@ -98,8 +101,14 @@ class TestRootBind(object): shared_mount.bind_mount.assert_called_once_with() @patch('kiwi.command.Command.run') + @patch('kiwi.system.root_bind.Checksum') @patch('os.path.exists') - def test_intermediate_config(self, mock_exists, mock_command): + @patch_open + def test_intermediate_config( + self, mock_open, mock_exists, mock_Checksum, mock_command + ): + checksum = mock.Mock() + mock_Checksum.return_value = checksum mock_exists.return_value = True self.bind_root.setup_intermediate_config() assert mock_command.call_args_list == [ @@ -110,17 +119,27 @@ class TestRootBind(object): 'ln', '-s', '-f', 'foo.kiwi', 'root-dir/foo' ]) ] + checksum.sha256.assert_called_once_with() + mock_open.assert_called_once_with( + 'root-dir/foo.sha', 'w' + ) + @patch('kiwi.system.root_bind.Checksum') @patch('kiwi.system.root_bind.MountManager.is_mounted') @patch('kiwi.system.root_bind.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') @patch('os.path.islink') @patch('os.path.exists') @patch('shutil.move') + @patch('kiwi.logger.log.warning') def test_cleanup( - self, mock_move, mock_exists, mock_islink, mock_remove_hierarchy, - mock_command, mock_is_mounted + self, mock_log_warn, mock_move, mock_exists, mock_islink, + mock_remove_hierarchy, mock_command, mock_is_mounted, + mock_Checksum ): + checksum = mock.Mock() + checksum.matches.return_value = False + mock_Checksum.return_value = checksum os_exists_return_values = [False, True] def exists_side_effect(*args): @@ -133,18 +152,24 @@ class TestRootBind(object): self.mount_manager.umount_lazy.assert_called_once_with() mock_remove_hierarchy.assert_called_once_with('root-dir/mountpoint') mock_command.assert_called_once_with( - ['rm', '-f', 'root-dir/foo.kiwi', 'root-dir/foo'] + [ + 'rm', '-f', 'root-dir/foo.kiwi', + 'root-dir/foo.sha', 'root-dir/foo' + ] ) mock_move.assert_called_once_with( 'root-dir/foo.rpmnew', 'root-dir/foo' ) + assert mock_log_warn.called @patch('os.path.islink') @patch('kiwi.logger.log.warning') @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') + @patch('kiwi.system.root_bind.Checksum') def test_cleanup_continue_on_error( - self, mock_remove_hierarchy, mock_command, mock_warn, mock_islink + self, mock_Checksum, mock_remove_hierarchy, + mock_command, mock_warn, mock_islink ): mock_islink.return_value = True mock_remove_hierarchy.side_effect = Exception('rm') @@ -167,8 +192,9 @@ class TestRootBind(object): @patch('kiwi.logger.log.warning') @patch('kiwi.command.Command.run') @patch('kiwi.system.root_bind.Path.remove_hierarchy') + @patch('kiwi.system.root_bind.Checksum') def test_cleanup_nothing_mounted( - self, mock_remove_hierarchy, mock_command, mock_warn + self, mock_Checksum, mock_remove_hierarchy, mock_command, mock_warn ): self.mount_manager.is_mounted.return_value = False self.mount_manager.mountpoint = '/mountpoint'