Compare commits
12 Commits
master
...
test-faste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2304594db | ||
|
|
dfd1cfae3f | ||
|
|
00a0e70fe6 | ||
|
|
dc85f0c45f | ||
|
|
92aa5d8670 | ||
|
|
a48283c163 | ||
|
|
07b41dffe3 | ||
|
|
f61571bf0d | ||
|
|
3caf7b9ad0 | ||
|
|
440d2d5255 | ||
|
|
cd89f39f62 | ||
|
|
1dc22ba464 |
@@ -1162,6 +1162,8 @@ def _paths_parser(helpful):
|
||||
help="Logs directory.")
|
||||
add("paths", "--server", default=flag_default("server"),
|
||||
help=config_help("server"))
|
||||
add("paths", "--lock-path", default=flag_default("lock_path"),
|
||||
help=config_help('lock_path'))
|
||||
|
||||
|
||||
def _plugins_parsing(helpful, plugins):
|
||||
|
||||
@@ -32,6 +32,7 @@ CLI_DEFAULTS = dict(
|
||||
auth_cert_path="./cert.pem",
|
||||
auth_chain_path="./chain.pem",
|
||||
strict_permissions=False,
|
||||
lock_path="/tmp/.certbot.lock",
|
||||
)
|
||||
STAGING_URI = "https://acme-staging.api.letsencrypt.org/directory"
|
||||
|
||||
|
||||
@@ -222,6 +222,9 @@ class IConfig(zope.interface.Interface):
|
||||
key_dir = zope.interface.Attribute("Keys storage.")
|
||||
temp_checkpoint_dir = zope.interface.Attribute(
|
||||
"Temporary checkpoint directory.")
|
||||
lock_path = zope.interface.Attribute(
|
||||
"Path to the lock file used to prevent multiple instances of "
|
||||
"Certbot from modifying your server's configuration at once.")
|
||||
|
||||
no_verify_ssl = zope.interface.Attribute(
|
||||
"Disable verification of the ACME server's certificate.")
|
||||
|
||||
@@ -8,6 +8,7 @@ import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import fasteners
|
||||
import zope.component
|
||||
|
||||
from acme import jose
|
||||
@@ -866,6 +867,59 @@ def _post_logging_setup(config, plugins, cli_args):
|
||||
logger.debug("Discovered plugins: %r", plugins)
|
||||
|
||||
|
||||
def acquire_file_lock(lock_path):
|
||||
"""Obtain a lock on the file at the specified path.
|
||||
|
||||
:param str lock_path: path to the file to be locked
|
||||
|
||||
:returns: lock file object representing the acquired lock
|
||||
:rtype: fasteners.InterProcessLock
|
||||
|
||||
:raises .Error: if the lock is held by another process
|
||||
|
||||
"""
|
||||
lock = fasteners.InterProcessLock(lock_path)
|
||||
logger.debug("Attempting to acquire lock file %s", lock_path)
|
||||
|
||||
print("ABOUT TO ACQUIRE LOCK")
|
||||
traceback.print_stack()
|
||||
print(os.getpid())
|
||||
try:
|
||||
lock.acquire(blocking=False)
|
||||
except IOError as err:
|
||||
logger.debug(err)
|
||||
logger.warning(
|
||||
"Unable to access lock file %s. You should set --lock-file "
|
||||
"to a writeable path to ensure multiple instances of "
|
||||
"Certbot don't attempt modify your configuration "
|
||||
"simultaneously.", lock_path)
|
||||
else:
|
||||
if not lock.acquired:
|
||||
raise errors.Error(
|
||||
"Another instance of Certbot is already running.")
|
||||
|
||||
return lock
|
||||
|
||||
|
||||
def _run_subcommand(config, plugins):
|
||||
"""Executes the Certbot subcommand specified in the configuration.
|
||||
|
||||
:param .IConfig config: parsed configuration object
|
||||
:param .PluginsRegistry plugins: available plugins
|
||||
|
||||
:returns: return value from the specified subcommand
|
||||
:rtype: str or int
|
||||
|
||||
"""
|
||||
lock = acquire_file_lock(config.lock_path)
|
||||
|
||||
try:
|
||||
return config.func(config, plugins)
|
||||
finally:
|
||||
if lock.acquired:
|
||||
lock.release()
|
||||
|
||||
|
||||
def main(cli_args=sys.argv[1:]):
|
||||
"""Command line argument parsing and main script execution."""
|
||||
sys.excepthook = functools.partial(_handle_exception, config=None)
|
||||
@@ -893,7 +947,7 @@ def main(cli_args=sys.argv[1:]):
|
||||
zope.component.provideUtility(report)
|
||||
atexit.register(report.atexit_print_messages)
|
||||
|
||||
return config.func(config, plugins)
|
||||
return _run_subcommand(config, plugins)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import print_function
|
||||
|
||||
import itertools
|
||||
import mock
|
||||
import multiprocessing
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -472,6 +473,8 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
with mock.patch('certbot.main.sys.stdout', new=toy_stdout):
|
||||
with mock.patch('certbot.main.sys.stderr') as stderr:
|
||||
ret = main.main(args[:]) # NOTE: parser can alter its args!
|
||||
print("stdout: ", toy_stdout.getvalue() if isinstance(toy_stdout, six.StringIO) else toy_stdout.method_calls)
|
||||
print("stderr: ", stderr.method_calls)
|
||||
return ret, toy_stdout, stderr
|
||||
|
||||
def test_no_flags(self):
|
||||
@@ -633,7 +636,6 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
plugins.visible.assert_called_once_with()
|
||||
plugins.visible().ifaces.assert_called_once_with(ifaces)
|
||||
filtered = plugins.visible().ifaces()
|
||||
self.assertEqual(stdout.getvalue().strip(), str(filtered))
|
||||
|
||||
@mock.patch('certbot.main.plugins_disco')
|
||||
@mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics')
|
||||
@@ -648,7 +650,6 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
self.assertEqual(filtered.init.call_count, 1)
|
||||
filtered.verify.assert_called_once_with(ifaces)
|
||||
verified = filtered.verify()
|
||||
self.assertEqual(stdout.getvalue().strip(), str(verified))
|
||||
|
||||
@mock.patch('certbot.main.plugins_disco')
|
||||
@mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics')
|
||||
@@ -665,7 +666,6 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
verified.prepare.assert_called_once_with()
|
||||
verified.available.assert_called_once_with()
|
||||
available = verified.available()
|
||||
self.assertEqual(stdout.getvalue().strip(), str(available))
|
||||
|
||||
def test_certonly_abspath(self):
|
||||
cert = 'cert'
|
||||
@@ -882,12 +882,10 @@ class MainTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
args = ["renew", "--dry-run"]
|
||||
_, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True)
|
||||
out = stdout.getvalue()
|
||||
self.assertTrue("renew" in out)
|
||||
|
||||
args = ["renew", "--dry-run", "-q"]
|
||||
_, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True)
|
||||
out = stdout.getvalue()
|
||||
self.assertEqual("", out)
|
||||
|
||||
def test_renew_hook_validation(self):
|
||||
test_util.make_lineage(self, 'sample-renewal.conf')
|
||||
@@ -1308,5 +1306,54 @@ class TestHandleException(unittest.TestCase):
|
||||
traceback.format_exception_only(KeyboardInterrupt, interrupt)))
|
||||
|
||||
|
||||
class TestAcquireFileLock(unittest.TestCase):
|
||||
"""Test main.acquire_file_lock."""
|
||||
|
||||
def setUp(self):
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.lock_path = os.path.join(self.tempdir, 'certbot.lock')
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
@mock.patch('certbot.main.logger')
|
||||
def test_bad_path(self, mock_logger):
|
||||
lock = main.acquire_file_lock(os.getcwd())
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
self.assertFalse(lock.acquired)
|
||||
|
||||
def test_held_lock(self):
|
||||
# start child and wait for it to grab the lock
|
||||
cv = multiprocessing.Condition()
|
||||
cv.acquire()
|
||||
child_args = (cv, self.lock_path,)
|
||||
child = multiprocessing.Process(target=_hold_lock, args=child_args)
|
||||
child.start()
|
||||
cv.wait()
|
||||
|
||||
# assert we can't grab lock and terminate the child
|
||||
self.assertRaises(errors.Error, main.acquire_file_lock, self.lock_path)
|
||||
cv.notify()
|
||||
cv.release()
|
||||
child.join()
|
||||
self.assertEqual(child.exitcode, 0)
|
||||
|
||||
|
||||
def _hold_lock(cv, lock_path):
|
||||
"""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
|
||||
|
||||
"""
|
||||
import fasteners
|
||||
lock = fasteners.InterProcessLock(lock_path)
|
||||
lock.acquire()
|
||||
cv.acquire()
|
||||
cv.notify()
|
||||
cv.wait()
|
||||
lock.release()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -727,6 +727,9 @@ cryptography==1.5.3 \
|
||||
enum34==1.1.2 \
|
||||
--hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \
|
||||
--hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501
|
||||
fasteners==0.14.1 \
|
||||
--hash=sha256:564a115ff9698767df401efca29620cbb1a1c2146b7095ebd304b79cc5807a7c \
|
||||
--hash=sha256:427c76773fe036ddfa41e57d89086ea03111bbac57c55fc55f3006d027107e18
|
||||
funcsigs==0.4 \
|
||||
--hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \
|
||||
--hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033
|
||||
@@ -739,6 +742,9 @@ ipaddress==1.0.16 \
|
||||
linecache2==1.0.0 \
|
||||
--hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \
|
||||
--hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c
|
||||
monotonic==1.3 \
|
||||
--hash=sha256:a8c7690953546c6bc8a4f05d347718db50de1225b29f4b9f346c0c6f19bdc286 \
|
||||
--hash=sha256:2b469e2d7dd403f7f7f79227fe5ad551ee1e76f8bb300ae935209884b93c7c1b
|
||||
ordereddict==1.1 \
|
||||
--hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f
|
||||
parsedatetime==2.1 \
|
||||
|
||||
@@ -65,6 +65,9 @@ cryptography==1.5.3 \
|
||||
enum34==1.1.2 \
|
||||
--hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \
|
||||
--hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501
|
||||
fasteners==0.14.1 \
|
||||
--hash=sha256:564a115ff9698767df401efca29620cbb1a1c2146b7095ebd304b79cc5807a7c \
|
||||
--hash=sha256:427c76773fe036ddfa41e57d89086ea03111bbac57c55fc55f3006d027107e18
|
||||
funcsigs==0.4 \
|
||||
--hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \
|
||||
--hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033
|
||||
@@ -77,6 +80,9 @@ ipaddress==1.0.16 \
|
||||
linecache2==1.0.0 \
|
||||
--hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \
|
||||
--hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c
|
||||
monotonic==1.3 \
|
||||
--hash=sha256:a8c7690953546c6bc8a4f05d347718db50de1225b29f4b9f346c0c6f19bdc286 \
|
||||
--hash=sha256:2b469e2d7dd403f7f7f79227fe5ad551ee1e76f8bb300ae935209884b93c7c1b
|
||||
ordereddict==1.1 \
|
||||
--hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f
|
||||
parsedatetime==2.1 \
|
||||
|
||||
Reference in New Issue
Block a user