Add lockfile (#4449)

* add lock_file

* cleanup lock file

* Add LockFile tests

* add lock_dir

* add lock_dir_until_exit

* add set_up_core_dir and move lock_dir_until_exit

* Move lock_and_call to certbot.test.util

* Add lock to Apache

* Add lock to the Nginx plugin

* Improve permissions error message

* sort plugins

* add test_prepare_order

* provide more actionable permissions error

* Document and catch use of OSError

* don't lock a directory twice

* add conditional dependency on ordereddict

* Add lock_test

* expand sorted plugins comment

* Add lock_test to lint

* make make_lineage more conventional and flexible

* enhance lock_test.py

* add lock_test to tox

* Readd success message

* make py26 happy

* add test_acquire_without_deletion
This commit is contained in:
Brad Warren
2017-05-01 14:49:12 -07:00
committed by Peter Eckersley
parent 4ca702f6fb
commit 5ca8f7c5b9
20 changed files with 750 additions and 46 deletions
+54 -6
View File
@@ -3,6 +3,7 @@
.. warning:: This module is not part of the public API.
"""
import multiprocessing
import os
import pkg_resources
import shutil
@@ -13,12 +14,14 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import mock
import OpenSSL
from six.moves import reload_module # pylint: disable=import-error
from acme import jose
from certbot import constants
from certbot import interfaces
from certbot import storage
from certbot import util
from certbot.display import util as display_util
@@ -106,12 +109,13 @@ def skip_unless(condition, reason): # pragma: no cover
return lambda cls: None
def make_lineage(self, testfile):
def make_lineage(config_dir, testfile):
"""Creates a lineage defined by testfile.
This creates the archive, live, and renewal directories if
necessary and creates a simple lineage.
:param str config_dir: path to the configuration directory
:param str testfile: configuration file to base the lineage on
:returns: path to the renewal conf file for the created lineage
@@ -121,11 +125,11 @@ def make_lineage(self, testfile):
lineage_name = testfile[:-len('.conf')]
conf_dir = os.path.join(
self.config_dir, constants.RENEWAL_CONFIGS_DIR)
config_dir, constants.RENEWAL_CONFIGS_DIR)
archive_dir = os.path.join(
self.config_dir, constants.ARCHIVE_DIR, lineage_name)
config_dir, constants.ARCHIVE_DIR, lineage_name)
live_dir = os.path.join(
self.config_dir, constants.LIVE_DIR, lineage_name)
config_dir, constants.LIVE_DIR, lineage_name)
for directory in (archive_dir, conf_dir, live_dir,):
if not os.path.exists(directory):
@@ -140,11 +144,11 @@ def make_lineage(self, testfile):
os.symlink(os.path.join(archive_dir, '{0}1.pem'.format(kind)),
os.path.join(live_dir, '{0}.pem'.format(kind)))
conf_path = os.path.join(self.config_dir, conf_dir, testfile)
conf_path = os.path.join(config_dir, conf_dir, testfile)
with open(vector_path(testfile)) as src:
with open(conf_path, 'w') as dst:
dst.writelines(
line.replace('MAGICDIR', self.config_dir) for line in src)
line.replace('MAGICDIR', config_dir) for line in src)
return conf_path
@@ -241,3 +245,47 @@ class TempDirTestCase(unittest.TestCase):
def tearDown(self):
shutil.rmtree(self.tempdir)
def lock_and_call(func, lock_path):
"""Grab a lock for lock_path and call func.
:param callable func: object to call after acquiring the lock
:param str lock_path: path to file or directory to lock
"""
# Reload module to reset internal _LOCKS dictionary
reload_module(util)
# start child and wait for it to grab the lock
cv = multiprocessing.Condition()
cv.acquire()
child_args = (cv, lock_path,)
child = multiprocessing.Process(target=hold_lock, args=child_args)
child.start()
cv.wait()
# call func and terminate the child
func()
cv.notify()
cv.release()
child.join()
assert child.exitcode == 0
def hold_lock(cv, lock_path): # pragma: no cover
"""Acquire a file lock at lock_path and wait to release it.
:param multiprocessing.Condition cv: condition for syncronization
:param str lock_path: path to the file lock
"""
from certbot import lock
if os.path.isdir(lock_path):
my_lock = lock.lock_dir(lock_path)
else:
my_lock = lock.LockFile(lock_path)
cv.acquire()
cv.notify()
cv.wait()
my_lock.release()