From ae1ee31b992b16a0ffa42e38a91a9bf619576bd7 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Sat, 24 Oct 2020 06:28:05 +1000 Subject: [PATCH] Fix race condition when creating async dir (#72069) (#72259) * Fix race condition when creating async dir * Simplify exception wrapper * Remove var used for testing (cherry picked from commit c9fa1d0e7ef981d0869d5d7a6c06245299d8ec65) --- changelogs/fragments/async-race-condition.yml | 2 ++ lib/ansible/modules/async_wrapper.py | 28 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 changelogs/fragments/async-race-condition.yml diff --git a/changelogs/fragments/async-race-condition.yml b/changelogs/fragments/async-race-condition.yml new file mode 100644 index 00000000000..c4690eb359f --- /dev/null +++ b/changelogs/fragments/async-race-condition.yml @@ -0,0 +1,2 @@ +bugfixes: +- async_wrapper - Fix race condition when ``~/.ansible_async`` folder tries to be created by multiple async tasks at the same time - https://github.com/ansible/ansible/issues/59306 diff --git a/lib/ansible/modules/async_wrapper.py b/lib/ansible/modules/async_wrapper.py index 26f0ef08b55..640e74cf93c 100644 --- a/lib/ansible/modules/async_wrapper.py +++ b/lib/ansible/modules/async_wrapper.py @@ -8,6 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +import errno import json import shlex import shutil @@ -123,6 +124,15 @@ def _get_interpreter(module_path): module_fd.close() +def _make_temp_dir(path): + # TODO: Add checks for permissions on path. + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + def _run_module(wrapped_cmd, jid, job_path): tmp_job_path = job_path + ".tmp" @@ -231,14 +241,16 @@ def main(): jobdir = os.path.expanduser(async_dir) job_path = os.path.join(jobdir, jid) - if not os.path.exists(jobdir): - try: - os.makedirs(jobdir) - except Exception: - print(json.dumps({ - "failed": 1, - "msg": "could not create: %s" % jobdir - })) + try: + _make_temp_dir(jobdir) + except Exception as e: + print(json.dumps({ + "failed": 1, + "msg": "could not create: %s - %s" % (jobdir, to_text(e)), + "exception": to_text(traceback.format_exc()), + })) + sys.exit(1) + # immediately exit this process, leaving an orphaned process # running which immediately forks a supervisory timing process