From 420527bebfe535ecb646b5f40e347b767560a6ce Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Mon, 12 Jul 2021 11:33:08 -0400 Subject: [PATCH] [stable-2.11] module_common - handle cache directory creation collision (#75106) (#75132) * module_common - handle cache directory creation collision Occasionally multiple workers can try to create the cache directory if it exists. Catch the exception and handle it. * Just re-raise if it doesn't exist rather than trying again * Use exists_ok rather than handling an exception * Remove unused import and unused variable * Go back to try/except but with OSError Since we do not have split controller remote, this needs to be able to run in Python 2 in order to be merged currently. (cherry picked from commit 16a74b798c) Co-authored-by: Sam Doran --- .../fragments/concurrency-cache-dir-collision.yml | 2 ++ lib/ansible/executor/module_common.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/concurrency-cache-dir-collision.yml diff --git a/changelogs/fragments/concurrency-cache-dir-collision.yml b/changelogs/fragments/concurrency-cache-dir-collision.yml new file mode 100644 index 00000000000..fbf62699dc1 --- /dev/null +++ b/changelogs/fragments/concurrency-cache-dir-collision.yml @@ -0,0 +1,2 @@ +bugfixes: + - module_common - handle exception when multiple workers try to create the cache directory diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index ffabb7445a6..ce114ce4887 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -34,7 +34,7 @@ from io import BytesIO from ansible.release import __version__, __author__ from ansible import constants as C -from ansible.errors import AnsibleError, AnsiblePluginRemovedError +from ansible.errors import AnsibleError from ansible.executor.interpreter_discovery import InterpreterDiscoveryRequiredError from ansible.executor.powershell import module_manifest as ps_manifest from ansible.module_utils.common.json import AnsibleJSONEncoder @@ -1115,7 +1115,6 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas return b_module_data, module_style, shebang output = BytesIO() - py_module_names = set() try: remote_module_fqn = _get_ansible_module_fqn(module_path) @@ -1186,9 +1185,15 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas # so that no one looking for the file reads a partially # written file) if not os.path.exists(lookup_path): - # Note -- if we have a global function to setup, that would - # be a better place to run this - os.makedirs(lookup_path) + try: + # Note -- if we have a global function to setup, that would + # be a better place to run this + os.makedirs(lookup_path) + except OSError: + # Multiple processes tried to create the directory. If it still does not + # exist, raise the original exception. + if not os.path.exists(lookup_path): + raise display.debug('ANSIBALLZ: Writing module') with open(cached_module_filename + '-part', 'wb') as f: f.write(zipdata)