From 7a14cb61c40102ae271474576bc142f73bfb6530 Mon Sep 17 00:00:00 2001 From: jeromew Date: Fri, 17 Jan 2014 08:14:25 +0000 Subject: [PATCH 1/5] Add creates= and removes= to the script: action --- lib/ansible/runner/action_plugins/script.py | 53 ++++++++++++++++++++- library/commands/script | 18 +++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/lib/ansible/runner/action_plugins/script.py b/lib/ansible/runner/action_plugins/script.py index 6b584c15b63..67b0cac9f0d 100644 --- a/lib/ansible/runner/action_plugins/script.py +++ b/lib/ansible/runner/action_plugins/script.py @@ -16,6 +16,7 @@ # along with Ansible. If not, see . import os +import re import shlex import ansible.constants as C @@ -38,12 +39,60 @@ class ActionModule(object): # in check mode, always skip this module return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not supported for this module')) + # extract ansible reserved parameters + # From library/command keep in sync + creates = None + removes = None + r = re.compile(r'(^|\s)(creates|removes)=(?P[\'"])?(.*?)(?(quote)(? Date: Mon, 24 Feb 2014 10:57:22 -0600 Subject: [PATCH 2/5] Added script to the new integration tests. --- test/integration/non_destructive.yml | 3 +- .../roles/test_script/files/create_afile.sh | 3 + .../roles/test_script/files/remove_afile.sh | 3 + .../roles/test_script/files/test.sh | 3 + .../roles/test_script/meta/main.yml | 3 + .../roles/test_script/tasks/main.yml | 69 +++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100755 test/integration/roles/test_script/files/create_afile.sh create mode 100755 test/integration/roles/test_script/files/remove_afile.sh create mode 100755 test/integration/roles/test_script/files/test.sh create mode 100644 test/integration/roles/test_script/meta/main.yml create mode 100644 test/integration/roles/test_script/tasks/main.yml diff --git a/test/integration/non_destructive.yml b/test/integration/non_destructive.yml index 7dc17e76389..0643a7be9d9 100644 --- a/test/integration/non_destructive.yml +++ b/test/integration/non_destructive.yml @@ -33,4 +33,5 @@ - { role: test_lookups, tags: test_lookups } - { role: test_iterators, tags: test_iterators } - { role: test_command_shell, tags: test_command_shell } - - { role: test_failed_when, tags: test_failed_when } \ No newline at end of file + - { role: test_failed_when, tags: test_failed_when } + - { role: test_script, tags: test_script } \ No newline at end of file diff --git a/test/integration/roles/test_script/files/create_afile.sh b/test/integration/roles/test_script/files/create_afile.sh new file mode 100755 index 00000000000..e6fae448b20 --- /dev/null +++ b/test/integration/roles/test_script/files/create_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "win" > "$1" \ No newline at end of file diff --git a/test/integration/roles/test_script/files/remove_afile.sh b/test/integration/roles/test_script/files/remove_afile.sh new file mode 100755 index 00000000000..4a7fea66174 --- /dev/null +++ b/test/integration/roles/test_script/files/remove_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +rm "$1" \ No newline at end of file diff --git a/test/integration/roles/test_script/files/test.sh b/test/integration/roles/test_script/files/test.sh new file mode 100755 index 00000000000..ade17e9b8c4 --- /dev/null +++ b/test/integration/roles/test_script/files/test.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo -n "win" \ No newline at end of file diff --git a/test/integration/roles/test_script/meta/main.yml b/test/integration/roles/test_script/meta/main.yml new file mode 100644 index 00000000000..1050c23ce30 --- /dev/null +++ b/test/integration/roles/test_script/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/roles/test_script/tasks/main.yml b/test/integration/roles/test_script/tasks/main.yml new file mode 100644 index 00000000000..e824c715ec6 --- /dev/null +++ b/test/integration/roles/test_script/tasks/main.yml @@ -0,0 +1,69 @@ +# Test code for the command and shell modules. +# (c) 2014, Richard Isaacson + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +## +## prep +## + +- set_fact: output_dir_test={{output_dir}}/test_script + +- name: make sure our testing sub-directory does not exist + file: path="{{ output_dir_test }}" state=absent + +- name: create our testing sub-directory + file: path="{{ output_dir_test }}" state=directory + +## +## script +## + +- name: execute the test.sh script via command + script: test.sh + register: script_result0 + +- name: assert that the script executed correctly + assert: + that: + - "script_result0.rc == 0" + - "script_result0.stderr == ''" + - "script_result0.stdout == 'win'" + +# creates + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + +- name: create afile.txt with create_afile.sh via command + shell: create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt + +- name: verify that afile.txt is present + file: path={{output_dir_test}}/afile.txt state=file + +# removes + +- name: remove afile.txt with remote_afile.sh via command + shell: remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + register: script_result1 + +- name: assert that the file was removed by the script + assert: + that: + - "script_result1.changed != True" From 0446a030d7937f12e620092bf0ea5a7a8efe2001 Mon Sep 17 00:00:00 2001 From: Richard C Isaacson Date: Mon, 24 Feb 2014 11:06:22 -0600 Subject: [PATCH 3/5] Typo fixes. --- test/integration/roles/test_script/tasks/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/roles/test_script/tasks/main.yml b/test/integration/roles/test_script/tasks/main.yml index e824c715ec6..3ea4d63f535 100644 --- a/test/integration/roles/test_script/tasks/main.yml +++ b/test/integration/roles/test_script/tasks/main.yml @@ -49,7 +49,7 @@ file: path={{output_dir_test}}/afile.txt state=absent - name: create afile.txt with create_afile.sh via command - shell: create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt + script: create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt - name: verify that afile.txt is present file: path={{output_dir_test}}/afile.txt state=file @@ -57,7 +57,7 @@ # removes - name: remove afile.txt with remote_afile.sh via command - shell: remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt + script: remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt - name: verify that afile.txt is absent file: path={{output_dir_test}}/afile.txt state=absent From 087f7c230df242c575cf5d041d6f3fabadd01ea1 Mon Sep 17 00:00:00 2001 From: Richard C Isaacson Date: Mon, 24 Feb 2014 11:08:21 -0600 Subject: [PATCH 4/5] Typo fixes. --- test/integration/roles/test_script/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/roles/test_script/tasks/main.yml b/test/integration/roles/test_script/tasks/main.yml index 3ea4d63f535..ffbe40d6007 100644 --- a/test/integration/roles/test_script/tasks/main.yml +++ b/test/integration/roles/test_script/tasks/main.yml @@ -1,4 +1,4 @@ -# Test code for the command and shell modules. +# Test code for the script module and action_plugin. # (c) 2014, Richard Isaacson # This file is part of Ansible From 8be8dbc9ed9da3ad0693aa4bf8f5f3d7fee4c4de Mon Sep 17 00:00:00 2001 From: Richard C Isaacson Date: Mon, 24 Feb 2014 11:11:47 -0600 Subject: [PATCH 5/5] Formatting cleanup. --- lib/ansible/runner/action_plugins/script.py | 56 +++++++++++---------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/ansible/runner/action_plugins/script.py b/lib/ansible/runner/action_plugins/script.py index 67b0cac9f0d..149be3cc113 100644 --- a/lib/ansible/runner/action_plugins/script.py +++ b/lib/ansible/runner/action_plugins/script.py @@ -25,8 +25,8 @@ from ansible import utils from ansible import errors from ansible.runner.return_data import ReturnData -class ActionModule(object): +class ActionModule(object): TRANSFERS_FILES = True def __init__(self, runner): @@ -37,7 +37,8 @@ class ActionModule(object): if self.runner.noop_on_check(inject): # in check mode, always skip this module - return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True, msg='check mode not supported for this module')) + return ReturnData(conn=conn, comm_ok=True, + result=dict(skipped=True, msg='check mode not supported for this module')) # extract ansible reserved parameters # From library/command keep in sync @@ -52,68 +53,69 @@ class ActionModule(object): removes = v module_args = r.sub("", module_args) - if creates: # do not run the command if the line contains creates=filename # and the filename already exists. This allows idempotence # of command executions. module_args_tmp = "path=%s" % creates - module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject, complex_args=complex_args, persist_files=True) + module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject, + complex_args=complex_args, persist_files=True) stat = module_return.result.get('stat', None) if stat and stat.get('exists', False): return ReturnData( - conn=conn, - comm_ok=True, - result=dict( - skipped=True, - msg=("skipped, since %s exists" % creates) - ) - ) + conn=conn, + comm_ok=True, + result=dict( + skipped=True, + msg=("skipped, since %s exists" % creates) + ) + ) if removes: # do not run the command if the line contains removes=filename # and the filename does not exist. This allows idempotence # of command executions. module_args_tmp = "path=%s" % removes - module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject, complex_args=complex_args, persist_files=True) + module_return = self.runner._execute_module(conn, tmp, 'stat', module_args_tmp, inject=inject, + complex_args=complex_args, persist_files=True) stat = module_return.result.get('stat', None) if stat and not stat.get('exists', False): return ReturnData( - conn=conn, - comm_ok=True, - result=dict( - skipped=True, - msg=("skipped, since %s does not exist" % removes) - ) - ) + conn=conn, + comm_ok=True, + result=dict( + skipped=True, + msg=("skipped, since %s does not exist" % removes) + ) + ) # Decode the result of shlex.split() to UTF8 to get around a bug in that's been fixed in Python 2.7 but not Python 2.6. # See: http://bugs.python.org/issue6988 - tokens = shlex.split(module_args.encode('utf8')) + tokens = shlex.split(module_args.encode('utf8')) tokens = [s.decode('utf8') for s in tokens] # extract source script - source = tokens[0] + source = tokens[0] # FIXME: error handling - args = " ".join(tokens[1:]) - source = template.template(self.runner.basedir, source, inject) + args = " ".join(tokens[1:]) + source = template.template(self.runner.basedir, source, inject) if '_original_file' in inject: source = utils.path_dwim_relative(inject['_original_file'], 'files', source, self.runner.basedir) else: source = utils.path_dwim(self.runner.basedir, source) # transfer the file to a remote tmp location - source = source.replace('\x00','') # why does this happen here? - args = args.replace('\x00','') # why does this happen here? + source = source.replace('\x00', '') # why does this happen here? + args = args.replace('\x00', '') # why does this happen here? tmp_src = os.path.join(tmp, os.path.basename(source)) tmp_src = tmp_src.replace('\x00', '') conn.put_file(source, tmp_src) - sudoable=True + sudoable = True # set file permissions, more permisive when the copy is done as a different user if self.runner.sudo and self.runner.sudo_user != 'root': cmd_args_chmod = "chmod a+rx %s" % tmp_src - sudoable=False + sudoable = False else: cmd_args_chmod = "chmod +rx %s" % tmp_src self.runner._low_level_exec_command(conn, cmd_args_chmod, tmp, sudoable=sudoable)