diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index e0daa5d48018..1e49246142be 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -2904,6 +2904,7 @@ def run_chroot(root, group=None, shell=DEFAULT_SHELL, python_shell=True, + binds=None, env=None, clean_env=False, template=None, @@ -2929,19 +2930,17 @@ def run_chroot(root, :param str root: Path to the root of the jail to use. - stdin - A string of standard input can be specified for the command to be run using - the ``stdin`` parameter. This can be useful in cases where sensitive - information must be read from standard input.: + :param str stdin: A string of standard input can be specified for + the command to be run using the ``stdin`` parameter. This can + be useful in cases where sensitive information must be read + from standard input.: - runas - User to run script as. + :param str runas: User to run script as. - group - Group to run script as. + :param str group: Group to run script as. - shell - Shell to execute under. Defaults to the system default shell. + :param str shell: Shell to execute under. Defaults to the system + default shell. :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -2965,6 +2964,11 @@ def run_chroot(root, arguments. Set to True to use shell features, such as pipes or redirection. + :param list binds: List of directories that will be exported inside + the chroot with the bind option. + + .. versionadded:: Sodium + :param dict env: Environment variables to be set prior to execution. .. note:: @@ -2983,11 +2987,11 @@ def run_chroot(root, engine will be used to render the downloaded file. Currently jinja, mako, and wempy are supported. - :param bool rstrip: - Strip all whitespace off the end of output before it is returned. + :param bool rstrip: Strip all whitespace off the end of output + before it is returned. - :param str umask: - The umask (in octal) to use when running the command. + :param str umask: The umask (in octal) to use when running the + command. :param str output_encoding: Control the encoding used to decode the command's output. @@ -3061,6 +3065,15 @@ def run_chroot(root, 'sysfs', fstype='sysfs') + binds = binds if binds else [] + for bind_exported in binds: + bind_exported_to = os.path.relpath(bind_exported, os.path.sep) + bind_exported_to = os.path.join(root, bind_exported_to) + __salt__['mount.mount']( + bind_exported_to, + bind_exported, + opts='default,bind') + # Execute chroot routine sh_ = '/bin/sh' if os.path.isfile(os.path.join(root, 'bin/bash')): @@ -3111,6 +3124,11 @@ def run_chroot(root, log.error('Processes running in chroot could not be killed, ' 'filesystem will remain mounted') + for bind_exported in binds: + bind_exported_to = os.path.relpath(bind_exported, os.path.sep) + bind_exported_to = os.path.join(root, bind_exported_to) + __salt__['mount.umount'](bind_exported_to) + __salt__['mount.umount'](os.path.join(root, 'sys')) __salt__['mount.umount'](os.path.join(root, 'proc')) __salt__['mount.umount'](os.path.join(root, 'dev')) diff --git a/tests/unit/modules/test_cmdmod.py b/tests/unit/modules/test_cmdmod.py index 8da672dd229c..492252127755 100644 --- a/tests/unit/modules/test_cmdmod.py +++ b/tests/unit/modules/test_cmdmod.py @@ -433,3 +433,33 @@ def test_run_all_output_encoding(self): ret = cmdmod.run_all('some command', output_encoding='latin1') self.assertEqual(ret['stdout'], stdout) + + def test_run_chroot_mount(self): + ''' + Test cmdmod.run_chroot mount / umount balance + ''' + mock_mount = MagicMock() + mock_umount = MagicMock() + mock_run_all = MagicMock() + with patch.dict(cmdmod.__salt__, { + 'mount.mount': mock_mount, + 'mount.umount': mock_umount}): + with patch('salt.modules.cmdmod.run_all', mock_run_all): + cmdmod.run_chroot('/mnt', 'cmd') + self.assertEqual(mock_mount.call_count, 3) + self.assertEqual(mock_umount.call_count, 3) + + def test_run_chroot_mount_bind(self): + ''' + Test cmdmod.run_chroot mount / umount balance with bind mount + ''' + mock_mount = MagicMock() + mock_umount = MagicMock() + mock_run_all = MagicMock() + with patch.dict(cmdmod.__salt__, { + 'mount.mount': mock_mount, + 'mount.umount': mock_umount}): + with patch('salt.modules.cmdmod.run_all', mock_run_all): + cmdmod.run_chroot('/mnt', 'cmd', binds=['/var']) + self.assertEqual(mock_mount.call_count, 4) + self.assertEqual(mock_umount.call_count, 4)