From 0fbbafb581e0ac697918d21a970b0db44f2f48d0 Mon Sep 17 00:00:00 2001 From: Sloane Hertel <19572925+s-hertel@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:53:37 -0400 Subject: [PATCH] password lookup - re-attempt acquiring lock file regardless of locale (#85318) (#85349) * Fix handling FileExistsError, instead of only handling OSError when the human-readable error message is "File exists". (cherry picked from commit 8e9f5fb9d5a061ba3238a22812e2e9e45cd3fab6) Co-authored-by: Matt Clay --- .../fix-lookup-password-lock-acquisition.yml | 2 ++ lib/ansible/plugins/lookup/password.py | 10 ++++------ test/units/plugins/lookup/test_password.py | 15 ++++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 changelogs/fragments/fix-lookup-password-lock-acquisition.yml diff --git a/changelogs/fragments/fix-lookup-password-lock-acquisition.yml b/changelogs/fragments/fix-lookup-password-lock-acquisition.yml new file mode 100644 index 00000000000..9152917a53b --- /dev/null +++ b/changelogs/fragments/fix-lookup-password-lock-acquisition.yml @@ -0,0 +1,2 @@ +bugfixes: + - password lookup - fix acquiring the lock when human-readable FileExistsError error message is not English. diff --git a/lib/ansible/plugins/lookup/password.py b/lib/ansible/plugins/lookup/password.py index 84894e21e9a..73b34891145 100644 --- a/lib/ansible/plugins/lookup/password.py +++ b/lib/ansible/plugins/lookup/password.py @@ -126,6 +126,7 @@ _raw: elements: str """ +import contextlib import os import string import time @@ -269,15 +270,12 @@ def _get_lock(b_path): b_pathdir = os.path.dirname(b_path) lockfile_name = to_bytes("%s.ansible_lockfile" % hashlib.sha1(b_path).hexdigest()) lockfile = os.path.join(b_pathdir, lockfile_name) - if not os.path.exists(lockfile) and b_path != to_bytes('/dev/null'): - try: - makedirs_safe(b_pathdir, mode=0o700) + if b_path != b'/dev/null': + makedirs_safe(b_pathdir, mode=0o700) + with contextlib.suppress(FileExistsError): fd = os.open(lockfile, os.O_CREAT | os.O_EXCL) os.close(fd) first_process = True - except OSError as e: - if e.strerror != 'File exists': - raise counter = 0 # if the lock is got by other process, wait until it's released diff --git a/test/units/plugins/lookup/test_password.py b/test/units/plugins/lookup/test_password.py index 46c63db6032..cd123e88c91 100644 --- a/test/units/plugins/lookup/test_password.py +++ b/test/units/plugins/lookup/test_password.py @@ -466,11 +466,16 @@ class TestLookupModuleWithoutPasslib(BaseTestLookupModule): @patch('time.sleep') def test_lock_been_held(self, mock_sleep): # pretend the lock file is here - password.os.path.exists = lambda x: True - with pytest.raises(AnsibleError): - with patch.object(builtins, 'open', mock_open(read_data=b'hunter42 salt=87654321\n')) as m: - # should timeout here - self.password_lookup.run([u'/path/to/somewhere chars=anything'], None) + def _already_exists(*args, **kwargs): + raise FileExistsError("The lock is busy, wait and try again.") + + with ( + pytest.raises(AnsibleError, match='^Password lookup cannot get the lock in 7 seconds.*'), + patch.object(password.os, 'open', _already_exists), + patch.object(password.os.path, 'exists', lambda *args, **kwargs: True), + ): + # should timeout here + self.password_lookup.run([u'/path/to/somewhere chars=anything'], None) def test_lock_not_been_held(self): # pretend now there is password file but no lock