Skip to content

Commit

Permalink
Run scripts via a screen session in debug mode
Browse files Browse the repository at this point in the history
When creating a custom script it usually takes some iterations of
try and testing until a final stable state is reached. To support
developers with this task kiwi calls scripts associated with a
screen session. The connection to screen is only done if kiwi
is called with the --debug option.
  • Loading branch information
schaefi committed Sep 22, 2021
1 parent a61a80e commit 7fa6cda
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 13 deletions.
47 changes: 47 additions & 0 deletions doc/source/concept_and_workflow/shell_scripts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,53 @@ bit is set (in that case a shebang is mandatory) otherwise they will be
invoked via the BASH. If a script exits with a non-zero exit code
then {kiwi} will report the failure and abort the image creation.

Developing/Debugging Scripts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When creating a custom script it usually takes some iterations of
try and testing until a final stable state is reached. To support
developers with this task {kiwi} calls scripts associated with a
`screen` session. The connection to `screen` is only done if {kiwi}
is called with the `--debug` option.

In this mode a script can start like the following template:

.. code:: bash
# The magic bits are still not set
echo "break"
/bin/bash
At call time of the script a `screen` session executes and you get
access to the break in shell. From this environment the needed script
code can be implemented. Once the shell is closed the {kiwi} process
continues.

Apart from providing a full featured terminal throughout the
execution of the script code, there is also the advantage to
have control on the session during the process of the image
creation. Listing the active sessions for script execution
can be done as follows:

.. code:: bash
$ sudo screen -list
There is a screen on:
19699.pts-4.asterix (Attached)
1 Socket in /run/screens/S-root.
.. note::

As shown above the screen session(s) to execute script code
provides extended control which could also be considered a
security risk. Because of that {kiwi} only runs scripts through
`screen` when explicitly enabled via the `--debug` switch.
For production processes all scripts should run in their
native way and should not require a terminal to operate
correctly !

Script Template for config.sh / images.sh
-----------------------------------------

Expand Down
23 changes: 19 additions & 4 deletions kiwi/system/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,14 @@ def _call_script(self, name, option_list=None):
script_path = os.path.join(self.root_dir, 'image', name)
if os.path.exists(script_path):
options = option_list or []
command = ['chroot', self.root_dir]
if log.getLogLevel() == logging.DEBUG:
# In debug mode run scripts in a screen session to
# allow attaching and debugging
command = ['screen', '-t', '-X', 'chroot', self.root_dir]
else:
# In standard mode run scripts without a terminal
# associated to them
command = ['chroot', self.root_dir]
if not Path.access(script_path, os.X_OK):
command.append('bash')
command.append(
Expand Down Expand Up @@ -1031,9 +1038,17 @@ def _call_script_no_chroot(
'cd', working_directory, '&&',
'bash', '--norc', script_path, ' '.join(option_list)
]
config_script = Command.call(
['bash', '-c', ' '.join(bash_command)]
)
if log.getLogLevel() == logging.DEBUG:
# In debug mode run scripts in a screen session to
# allow attaching and debugging
config_script = Command.call(
['screen', '-t', '-X', 'bash', '-c', ' '.join(bash_command)]
)
else:
# In standard mode run script through bash
config_script = Command.call(
['bash', '-c', ' '.join(bash_command)]
)
process = CommandProcess(
command=config_script, log_topic='Calling ' + name + ' script'
)
Expand Down
2 changes: 1 addition & 1 deletion package/python-kiwi-pkgbuild-template
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ build() {
}

package_python-kiwi(){
depends=(python-docopt python-future python-lxml python-requests python-setuptools python-six python-pyxattr python-yaml grub qemu squashfs-tools gptfdisk pacman e2fsprogs xfsprogs btrfs-progs libisoburn lvm2 mtools parted multipath-tools rsync tar shadow kiwi-man-pages)
depends=(python-docopt python-future python-lxml python-requests python-setuptools python-six python-pyxattr python-yaml grub qemu squashfs-tools gptfdisk pacman e2fsprogs xfsprogs btrfs-progs libisoburn lvm2 mtools parted multipath-tools rsync tar shadow screen kiwi-man-pages)
optdepends=('gnupg: keyring creation for APT package manager')
cd kiwi-${pkgver}
python setup.py install --root="${pkgdir}/" --optimize=1 --skip-build
Expand Down
1 change: 1 addition & 0 deletions package/python-kiwi-spec-template
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Group: %{pygroup}
Obsoletes: python2-kiwi
Conflicts: python2-kiwi
Conflicts: kiwi-man-pages < %{version}
Requires: screen
Requires: python%{python3_pkgversion} >= 3.6
%if 0%{?ubuntu} || 0%{?debian}
Requires: python%{python3_pkgversion}-yaml
Expand Down
28 changes: 20 additions & 8 deletions test/unit/system/setup_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ def test_call_excutable_post_bootstrap_script(
['chroot', 'root_dir', 'image/post_bootstrap.sh'], {}
)

@patch('kiwi.logger.Logger.getLogLevel')
@patch('kiwi.system.setup.Profile')
@patch('kiwi.command.Command.call')
@patch('kiwi.command_process.CommandProcess.poll_and_watch')
Expand All @@ -714,8 +715,9 @@ def test_call_excutable_post_bootstrap_script(
@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_watch, mock_command, mock_Profile, mock_getLogLevel
):
mock_getLogLevel.return_value = logging.DEBUG
mock_copy_deepcopy.return_value = {}
profile = Mock()
mock_Profile.return_value = profile
Expand All @@ -731,7 +733,10 @@ def test_call_disk_script(
self.setup.call_disk_script()
mock_copy_deepcopy.assert_called_once_with(os.environ)
mock_command.assert_called_once_with(
['chroot', 'root_dir', 'bash', 'image/disk.sh'], {}
[
'screen', '-t', '-X',
'chroot', 'root_dir', 'bash', 'image/disk.sh'
], {}
)

@patch('kiwi.system.setup.Profile')
Expand Down Expand Up @@ -787,13 +792,16 @@ def test_call_edit_boot_config_script(
'ext4 1'
])

@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
self, mock_abspath, mock_exists, mock_watch, mock_command,
mock_getLogLevel
):
mock_getLogLevel.return_value = logging.DEBUG
result_type = namedtuple(
'result_type', ['stderr', 'returncode']
)
Expand All @@ -807,11 +815,15 @@ def test_call_edit_boot_install_script(
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'
])
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')
Expand Down

0 comments on commit 7fa6cda

Please sign in to comment.