Added warnings to command module

Generate warnings when users are shelling out to commands
rather than using modules

Can be turned off on a per-action line with the documented
warn=False flag. Can be turned off globally using
command_warnings = False in ansible config file.

Print out warnings using the standard playbook callbacks.

Created some additional tests in TestRunner.test_command
and also a demonstration playbook.
pull/8725/head
Will Thames 11 years ago committed by Michael DeHaan
parent f54665aa51
commit ab8490d003

@ -535,6 +535,9 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
display(msg, color='green', runner=self.runner)
else:
display(msg, color='yellow', runner=self.runner)
if constants.COMMAND_WARNINGS and 'warnings' in host_result2 and host_result2['warnings']:
for warning in host_result2['warnings']:
display("warn: %s" % warning, color='purple', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_ok(host, host_result)
def on_skipped(self, host, item=None):

@ -170,6 +170,7 @@ HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', '
SYSTEM_WARNINGS = get_config(p, DEFAULTS, 'system_warnings', 'ANSIBLE_SYSTEM_WARNINGS', True, boolean=True)
DEPRECATION_WARNINGS = get_config(p, DEFAULTS, 'deprecation_warnings', 'ANSIBLE_DEPRECATION_WARNINGS', True, boolean=True)
DEFAULT_CALLABLE_WHITELIST = get_config(p, DEFAULTS, 'callable_whitelist', 'ANSIBLE_CALLABLE_WHITELIST', [], islist=True)
COMMAND_WARNINGS = get_config(p, DEFAULTS, 'command_warnings', 'ANSIBLE_COMMAND_WARNINGS', True, boolean=True)
# CONNECTION RELATED
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)

@ -67,6 +67,12 @@ options:
required: false
default: null
version_added: "0.9"
warn:
description:
- turn off warnings about running a command that is provided by an Ansible module.
required: false
default: True
version_added: "1.5"
notes:
- If you want to run a command through the shell (say you are using C(<),
C(>), C(|), etc), you actually want the M(shell) module instead. The
@ -108,7 +114,27 @@ EXAMPLES = '''
# that was matched in the first 'quote' is found, or the end of
# the line is reached
PARAM_REGEX = re.compile(r'(^|\s)(creates|removes|chdir|executable|NO_LOG)=(?P<quote>[\'"])?(.*?)(?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)')
PARAM_REGEX = re.compile(r'(^|\s)(creates|removes|chdir|executable|NO_LOG|warn)=(?P<quote>[\'"])?(.*?)(?(quote)(?<!\\)(?P=quote))((?<!\\)(?=\s)|$)')
def check_command(commandline):
arguments = { 'chown': 'owner', 'chmod': 'mode', 'chgrp': 'group',
'ln': 'state=link', 'mkdir': 'state=directory',
'rmdir': 'state=absent', 'rm': 'state=absent', 'touch': 'state=touch' }
commands = { 'git': 'git', 'hg': 'hg', 'curl': 'get_url', 'wget': 'get_url',
'svn': 'subversion', 'service': 'service',
'mount': 'mount', 'rpm': 'yum', 'yum': 'yum', 'apt-get': 'apt-get',
'tar': 'unarchive', 'unzip': 'unarchive', 'sed': 'template or lineinfile',
'echo': 'template or lineinfile', 'cp': 'synchronize or copy',
'rsync': 'synchronize' }
warnings = list()
command = os.path.basename(commandline.split()[0])
if command in arguments:
warnings.append("Consider using file module with %s rather than running %s" % (arguments[command], command))
if command in commands:
warnings.append("Consider using %s module rather than running %s" % (commands[command], command))
return warnings
def main():
@ -122,6 +148,7 @@ def main():
args = module.params['args']
creates = module.params['creates']
removes = module.params['removes']
warn = module.params.get('warn', True)
if args.strip() == '':
module.fail_json(rc=256, msg="no command given")
@ -157,6 +184,10 @@ def main():
rc=0
)
warnings = list()
if warn:
warnings = check_command(args)
if not shell:
args = shlex.split(args)
startd = datetime.datetime.now()
@ -172,14 +203,15 @@ def main():
err = ''
module.exit_json(
cmd = args,
stdout = out.rstrip("\r\n"),
stderr = err.rstrip("\r\n"),
rc = rc,
start = str(startd),
end = str(endd),
delta = str(delta),
changed = True
cmd = args,
stdout = out.rstrip("\r\n"),
stderr = err.rstrip("\r\n"),
rc = rc,
start = str(startd),
end = str(endd),
delta = str(delta),
changed = True,
warnings = warnings
)
# import module snippets
@ -206,6 +238,7 @@ class CommandModule(AnsibleModule):
params['removes'] = None
params['shell'] = False
params['executable'] = None
params['warn'] = True
if "#USE_SHELL" in args:
args = args.replace("#USE_SHELL", "")
params['shell'] = True
@ -236,6 +269,7 @@ class CommandModule(AnsibleModule):
# Remove any of the above k=v params from the args string
args = PARAM_REGEX.sub('', args)
params['args'] = args.strip()
return (params, params['args'])
main()

@ -41,6 +41,12 @@ options:
required: false
default: null
version_added: "0.9"
warn:
description:
- turn off warnings about running a command that is provided by an Ansible module.
required: false
default: True
version_added: "1.5"
notes:
- If you want to execute a command securely and predictably, it may be
better to use the M(command) module instead. Best practices when writing

@ -115,6 +115,7 @@
- "shell_result0.rc == 0"
- "shell_result0.stderr == ''"
- "shell_result0.stdout == 'win'"
- "not shell_result0.warnings"
# executable
@ -143,6 +144,7 @@
- "shell_result2.rc == 0"
- "shell_result2.stderr == ''"
- "shell_result2.stdout == 'win'"
- "not shell_result2.warnings"
# creates
@ -157,17 +159,23 @@
# removes
- name: execute the test.sh script with chdir
- name: remove afile.txt using rm
shell: rm {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt
register: shell_result4
- name: assert that using rm under shell causes a warning
assert:
that:
- "shell_result4.warnings"
- name: verify that afile.txt is absent
file: path={{output_dir_test}}/afile.txt state=absent
register: shell_result4
register: shell_result5
- name: assert that the file was removed by the shell
assert:
that:
- "shell_result4.changed == False"
- "shell_result5.changed == False"
- name: execute a shell command using a literal multiline block
args:
@ -181,13 +189,12 @@
| tr -s ' ' \
| cut -f1 -d ' '
echo "this is a second line"
register: shell_result5
register: shell_result6
- debug: var=shell_result5
- debug: var=shell_result6
- name: assert the multiline shell command ran as expected
assert:
that:
- "shell_result5.changed"
- "shell_result5.stdout == '32f3cc201b69ed8afa3902b80f554ca8\nthis is a second line'"
- "shell_result6.changed"
- "shell_result6.stdout == '32f3cc201b69ed8afa3902b80f554ca8\nthis is a second line'"

Loading…
Cancel
Save