From 80940a6b1c7b24cc4641625a007bd70921ecb584 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Wed, 20 Jun 2018 13:35:47 -0500 Subject: [PATCH] ensure copy action plugin returns an invocation in result (#41426) * ensure copy action plugin returns an invocation in result Fixes #18232 Previously the action plugin for copy, which runs operations on the control host to orchestrate executing both local actions on the control host and remote actions on the remote host, is not returning invocation dict in the result dict, this happens here where the return from _copy_file() is None When force is True, the _execute_module() method is called, which returns the dict containing the invocation key. This patch ensures there is always an invocation key. Signed-off-by: Adam Miller * fix conditional, handle content no_log Signed-off-by: Adam Miller --- lib/ansible/plugins/action/copy.py | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/ansible/plugins/action/copy.py b/lib/ansible/plugins/action/copy.py index b0502bfff4..13007d3e75 100644 --- a/lib/ansible/plugins/action/copy.py +++ b/lib/ansible/plugins/action/copy.py @@ -208,6 +208,21 @@ class ActionModule(ActionBase): TRANSFERS_FILES = True + def _ensure_invocation(self, result): + # NOTE: adding invocation arguments here needs to be kept in sync with + # any no_log specified in the argument_spec in the module. + # This is not automatic. + if 'invocation' not in result: + if self._play_context.no_log: + result['invocation'] = "CENSORED: no_log is set" + else: + result["invocation"] = self._task.args.copy() + + if isinstance(result['invocation'], dict) and 'content' in result['invocation']: + result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + + return result + def _copy_file(self, source_full, source_rel, content, content_tempfile, dest, task_vars, follow): decrypt = boolean(self._task.args.get('decrypt', True), strict=False) @@ -406,7 +421,7 @@ class ActionModule(ActionBase): del result['failed'] if result.get('failed'): - return result + return self._ensure_invocation(result) # Define content_tempfile in case we set it after finding content populated. content_tempfile = None @@ -424,13 +439,13 @@ class ActionModule(ActionBase): except Exception as err: result['failed'] = True result['msg'] = "could not write content temp file: %s" % to_native(err) - return result + return self._ensure_invocation(result) # if we have first_available_file in our vars # look up the files and use the first one we find as src elif remote_src: result.update(self._execute_module(module_name='copy', task_vars=task_vars)) - return result + return self._ensure_invocation(result) else: # find_needle returns a path that may not have a trailing slash on # a directory so we need to determine that now (we use it just @@ -444,7 +459,7 @@ class ActionModule(ActionBase): result['failed'] = True result['msg'] = to_text(e) result['exception'] = traceback.format_exc() - return result + return self._ensure_invocation(result) if trailing_slash != source.endswith(os.path.sep): if source[-1] == os.path.sep: @@ -498,7 +513,7 @@ class ActionModule(ActionBase): if module_return.get('failed'): result.update(module_return) - return result + return self._ensure_invocation(result) paths = os.path.split(source_rel) dir_path = '' @@ -528,7 +543,7 @@ class ActionModule(ActionBase): if module_return.get('failed'): result.update(module_return) - return result + return self._ensure_invocation(result) module_executed = True changed = changed or module_return.get('changed', False) @@ -550,7 +565,7 @@ class ActionModule(ActionBase): if module_return.get('failed'): result.update(module_return) - return result + return self._ensure_invocation(result) changed = changed or module_return.get('changed', False) @@ -567,4 +582,4 @@ class ActionModule(ActionBase): # Delete tmp path self._remove_tmp_path(self._connection._shell.tmpdir) - return result + return self._ensure_invocation(result)