Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

passwordstore plugin: vendor FileLock that was removed from ansible-core devel #6447

Merged
merged 1 commit into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/passwordstore-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- "passwordstore lookup plugin - make compatible with ansible-core 2.16 (https://github.com/ansible-collections/community.general/pull/6447)."
3 changes: 2 additions & 1 deletion plugins/lookup/passwordstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,15 @@
import yaml

from ansible.errors import AnsibleError, AnsibleAssertionError
from ansible.module_utils.common.file import FileLock
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.utils.display import Display
from ansible.utils.encrypt import random_password
from ansible.plugins.lookup import LookupBase
from ansible import constants as C

from ansible_collections.community.general.plugins.module_utils._filelock import FileLock

display = Display()


Expand Down
109 changes: 109 additions & 0 deletions plugins/module_utils/_filelock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright (c) 2018, Ansible Project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause

# NOTE:
# This has been vendored from ansible.module_utils.common.file. This code has been removed from there for ansible-core 2.16.

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import os
import stat
import time
import fcntl
import sys

from contextlib import contextmanager


class LockTimeout(Exception):
pass


class FileLock:
'''
Currently FileLock is implemented via fcntl.flock on a lock file, however this
behaviour may change in the future. Avoid mixing lock types fcntl.flock,
fcntl.lockf and module_utils.common.file.FileLock as it will certainly cause
unwanted and/or unexpected behaviour
'''
def __init__(self):
self.lockfd = None

@contextmanager
def lock_file(self, path, tmpdir, lock_timeout=None):
'''
Context for lock acquisition
'''
try:
self.set_lock(path, tmpdir, lock_timeout)
yield
finally:
self.unlock()

def set_lock(self, path, tmpdir, lock_timeout=None):
'''
Create a lock file based on path with flock to prevent other processes
using given path.
Please note that currently file locking only works when it's executed by
the same user, I.E single user scenarios

:kw path: Path (file) to lock
:kw tmpdir: Path where to place the temporary .lock file
:kw lock_timeout:
Wait n seconds for lock acquisition, fail if timeout is reached.
0 = Do not wait, fail if lock cannot be acquired immediately,
Default is None, wait indefinitely until lock is released.
:returns: True
'''
lock_path = os.path.join(tmpdir, 'ansible-{0}.lock'.format(os.path.basename(path)))
l_wait = 0.1
r_exception = IOError
if sys.version_info[0] == 3:
r_exception = BlockingIOError

self.lockfd = open(lock_path, 'w')

if lock_timeout <= 0:
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
return True

if lock_timeout:
e_secs = 0
while e_secs < lock_timeout:
try:
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
return True
except r_exception:
time.sleep(l_wait)
e_secs += l_wait
continue

self.lockfd.close()
raise LockTimeout('{0} sec'.format(lock_timeout))

fcntl.flock(self.lockfd, fcntl.LOCK_EX)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)

return True

def unlock(self):
'''
Make sure lock file is available for everyone and Unlock the file descriptor
locked by set_lock

:returns: True
'''
if not self.lockfd:
return True

try:
fcntl.flock(self.lockfd, fcntl.LOCK_UN)
self.lockfd.close()
except ValueError: # file wasn't opened, let context manager fail gracefully
pass

return True