Commit ab8490d0 by Will Thames Committed by Michael DeHaan

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.
parent f54665aa
......@@ -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'"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment