1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Merge pull request #14025 from ansible/run_command-env-rework

rework run_command's env setting to not change os.environ for the res…
This commit is contained in:
Toshio Kuratomi 2016-01-20 12:13:43 -08:00
commit d97d28ecb0
2 changed files with 41 additions and 31 deletions

View file

@ -546,11 +546,10 @@ class AnsibleModule(object):
if no_log_object: if no_log_object:
self.no_log_values.update(return_values(no_log_object)) self.no_log_values.update(return_values(no_log_object))
# check the locale as set by the current environment, and # check the locale as set by the current environment, and reset to
# reset to LANG=C if it's an invalid/unavailable locale # a known valid (LANG=C) if it's an invalid/unavailable locale
self._check_locale() self._check_locale()
self._check_arguments(check_invalid_arguments) self._check_arguments(check_invalid_arguments)
# check exclusive early # check exclusive early
@ -1094,7 +1093,6 @@ class AnsibleModule(object):
# as it would be returned by locale.getdefaultlocale() # as it would be returned by locale.getdefaultlocale()
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
except locale.Error: except locale.Error:
e = get_exception()
# fallback to the 'C' locale, which may cause unicode # fallback to the 'C' locale, which may cause unicode
# issues but is preferable to simply failing because # issues but is preferable to simply failing because
# of an unknown locale # of an unknown locale
@ -1757,25 +1755,29 @@ class AnsibleModule(object):
# rename might not preserve context # rename might not preserve context
self.set_context_if_different(dest, context, False) self.set_context_if_different(dest, context, False)
def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None, use_unsafe_shell=False, prompt_regex=None): def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None, use_unsafe_shell=False, prompt_regex=None, environ_update=None):
''' '''
Execute a command, returns rc, stdout, and stderr. Execute a command, returns rc, stdout, and stderr.
args is the command to run
If args is a list, the command will be run with shell=False. :arg args: is the command to run
If args is a string and use_unsafe_shell=False it will split args to a list and run with shell=False * If args is a list, the command will be run with shell=False.
If args is a string and use_unsafe_shell=True it run with shell=True. * If args is a string and use_unsafe_shell=False it will split args to a list and run with shell=False
Other arguments: * If args is a string and use_unsafe_shell=True it runs with shell=True.
- check_rc (boolean) Whether to call fail_json in case of :kw check_rc: Whether to call fail_json in case of non zero RC.
non zero RC. Default is False. Default False
- close_fds (boolean) See documentation for subprocess.Popen(). :kw close_fds: See documentation for subprocess.Popen(). Default True
Default is True. :kw executable: See documentation for subprocess.Popen(). Default None
- executable (string) See documentation for subprocess.Popen(). :kw data: If given, information to write to the stdin of the command
Default is None. :kw binary_data: If False, append a newline to the data. Default False
- prompt_regex (string) A regex string (not a compiled regex) which :kw path_prefix: If given, additional path to find the command in.
can be used to detect prompts in the stdout This adds to the PATH environment vairable so helper commands in
which would otherwise cause the execution the same directory can also be found
to hang (especially if no input data is :kw cwd: iIf given, working directory to run the command inside
specified) :kw use_unsafe_shell: See `args` parameter. Default False
:kw prompt_regex: Regex string (not a compiled regex) which can be
used to detect prompts in the stdout which would otherwise cause
the execution to hang (especially if no input data is specified)
:kwarg environ_update: dictionary to *update* os.environ with
''' '''
shell = False shell = False
@ -1806,10 +1808,15 @@ class AnsibleModule(object):
msg = None msg = None
st_in = None st_in = None
# Set a temporary env path if a prefix is passed # Manipulate the environ we'll send to the new process
env=os.environ old_env_vals = {}
if environ_update:
for key, val in environ_update.items():
old_env_vals[key] = os.environ.get(key, None)
os.environ[key] = val
if path_prefix: if path_prefix:
env['PATH']="%s:%s" % (path_prefix, env['PATH']) old_env_vals['PATH'] = os.environ['PATH']
os.environ['PATH'] = "%s:%s" % (path_prefix, os.environ['PATH'])
# create a printable version of the command for use # create a printable version of the command for use
# in reporting later, which strips out things like # in reporting later, which strips out things like
@ -1851,11 +1858,10 @@ class AnsibleModule(object):
close_fds=close_fds, close_fds=close_fds,
stdin=st_in, stdin=st_in,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE stderr=subprocess.PIPE,
env=os.environ,
) )
if path_prefix:
kwargs['env'] = env
if cwd and os.path.isdir(cwd): if cwd and os.path.isdir(cwd):
kwargs['cwd'] = cwd kwargs['cwd'] = cwd
@ -1934,6 +1940,13 @@ class AnsibleModule(object):
except: except:
self.fail_json(rc=257, msg=traceback.format_exc(), cmd=clean_args) self.fail_json(rc=257, msg=traceback.format_exc(), cmd=clean_args)
# Restore env settings
for key, val in old_env_vals.items():
if val is None:
del os.environ[key]
else:
os.environ[key] = val
if rc != 0 and check_rc: if rc != 0 and check_rc:
msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values) msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values)
self.fail_json(cmd=clean_args, rc=rc, stdout=stdout, stderr=stderr, msg=msg) self.fail_json(cmd=clean_args, rc=rc, stdout=stdout, stderr=stderr, msg=msg)

View file

@ -39,6 +39,7 @@ class OpenStringIO(StringIO):
def close(self): def close(self):
pass pass
@unittest.skipIf(sys.version_info[0] >= 3, "Python 3 is not supported on targets (yet)") @unittest.skipIf(sys.version_info[0] >= 3, "Python 3 is not supported on targets (yet)")
class TestAnsibleModuleRunCommand(unittest.TestCase): class TestAnsibleModuleRunCommand(unittest.TestCase):
@ -111,10 +112,6 @@ class TestAnsibleModuleRunCommand(unittest.TestCase):
self.assertEqual(args, ('ls a " b" "c "', )) self.assertEqual(args, ('ls a " b" "c "', ))
self.assertEqual(kwargs['shell'], True) self.assertEqual(kwargs['shell'], True)
def test_path_prefix(self):
self.module.run_command('foo', path_prefix='/opt/bin')
self.assertEqual('/opt/bin', self.os.environ['PATH'].split(':')[0])
def test_cwd(self): def test_cwd(self):
self.os.getcwd.return_value = '/old' self.os.getcwd.return_value = '/old'
self.module.run_command('/bin/ls', cwd='/new') self.module.run_command('/bin/ls', cwd='/new')