[checkmk-commits] Add context manager for PID file locking

Lars Michelsen lm at mathias-kettner.de
Thu Nov 8 16:30:38 CET 2018


Module: check_mk
Branch: master
Commit: 91cc92e7eee858c6aedaef614772ceaad69fd5c2
URL:    http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=91cc92e7eee858c6aedaef614772ceaad69fd5c2

Author: Lars Michelsen <lm at mathias-kettner.de>
Date:   Mon Nov  5 08:42:10 2018 +0100

Add context manager for PID file locking

* Added tests to verify PID locking mechanism

Change-Id: Id8c69cf894e6c1b34b76798af76882c97a1e4c6d

---

 cmk/daemon.py                 | 32 +++++++++++++++++++++++-
 tests/unit/cmk/test_daemon.py | 58 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/cmk/daemon.py b/cmk/daemon.py
index aac0e95..20faeb9 100644
--- a/cmk/daemon.py
+++ b/cmk/daemon.py
@@ -30,9 +30,11 @@ from pwd import getpwnam
 from grp import getgrnam
 import ctypes
 import ctypes.util
+from pathlib2 import Path # pylint: disable=unused-import
+from contextlib import contextmanager
 
 import cmk.store
-from .exceptions import MKGeneralException
+from cmk.exceptions import MKGeneralException
 
 
 def daemonize(user=0, group=0):
@@ -99,7 +101,9 @@ def closefrom(lowfd):
     os.closerange(lowfd, highfd)
 
 
+# TODO: Change API and call sites to work with Path() objects
 def lock_with_pid_file(path):
+    # type: (str) -> None
     """
     Use this after daemonizing or in foreground mode to ensure there is only
     one process running.
@@ -114,6 +118,32 @@ def lock_with_pid_file(path):
         f.write("%d\n" % os.getpid())
 
 
+# TODO: Change API and call sites to work with Path() objects
+def _cleanup_locked_pid_file(path):
+    # type: (str) -> None
+    """Cleanup the lock + file acquired by the function above"""
+    if not cmk.store.have_lock(path):
+        return
+
+    cmk.store.release_lock(path)
+
+    try:
+        os.remove(path)
+    except OSError:
+        pass
+
+
+ at contextmanager
+def pid_file_lock(path):
+    # type: (Path) -> None
+    """Context manager for PID file based locking"""
+    lock_with_pid_file("%s" % path)
+    try:
+        yield
+    finally:
+        _cleanup_locked_pid_file("%s" % path)
+
+
 def set_cmdline(cmdline):
     """
     Change the process name and process command line on of the running process
diff --git a/tests/unit/cmk/test_daemon.py b/tests/unit/cmk/test_daemon.py
new file mode 100644
index 0000000..7ca5599
--- /dev/null
+++ b/tests/unit/cmk/test_daemon.py
@@ -0,0 +1,58 @@
+import pytest
+from pathlib2 import Path
+import os
+
+import cmk.store as store
+import cmk.daemon as daemon
+from cmk.exceptions import MKGeneralException
+
+ at pytest.fixture(autouse=True)
+def cleanup_locks():
+    yield
+    store.release_all_locks()
+
+
+def test_lock_with_pid_file(tmpdir):
+    pid_file = Path(tmpdir) / "test.pid"
+
+    daemon.lock_with_pid_file("%s" % pid_file)
+
+    assert store.have_lock("%s" % pid_file)
+
+    with pid_file.open() as f:
+        assert int(f.read()) == os.getpid()
+
+
+def test_cleanup_locked_pid_file(tmpdir):
+    pid_file = Path(tmpdir) / "test.pid"
+
+    assert not store.have_lock("%s" % pid_file)
+    daemon.lock_with_pid_file("%s" % pid_file)
+    assert store.have_lock("%s" % pid_file)
+
+    daemon._cleanup_locked_pid_file("%s" % pid_file)
+
+    assert not store.have_lock("%s" % pid_file)
+
+
+def test_pid_file_lock_context_manager(tmpdir):
+    pid_file = Path(tmpdir) / "test.pid"
+
+    assert not store.have_lock("%s" % pid_file)
+
+    with daemon.pid_file_lock(pid_file):
+        assert store.have_lock("%s" % pid_file)
+
+
+def test_pid_file_lock_context_manager_exception(tmpdir):
+    pid_file = Path(tmpdir) / "test.pid"
+
+    assert not store.have_lock("%s" % pid_file)
+    try:
+        with daemon.pid_file_lock(pid_file):
+            assert store.have_lock("%s" % pid_file)
+            raise MKGeneralException("bla")
+    except MKGeneralException:
+        pass
+
+    assert not store.have_lock("%s" % pid_file)



More information about the checkmk-commits mailing list