From 16a74b798c9eebe2c1df49365270671fb674230d Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Fri, 25 Jun 2021 18:36:12 -0400 Subject: [PATCH] module_common - handle cache directory creation collision (#75106) * 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. --- .../concurrency-cache-dir-collision.yml | 2 ++ lib/ansible/executor/module_common.py | 18 +++++++++++++----- 2 files changed, 15 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 5871c66d45d..fd9d857c887 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) @@ -1185,10 +1184,19 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas # Write the assembled module to a temp file (write to temp # so that no one looking for the file reads a partially # written file) + # + # FIXME: Once split controller/remote is merged, this can be simplified to + # os.makedirs(lookup_path, exist_ok=True) 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)