Add lockfile attribute
This commit is contained in:
parent
f2bf042549
commit
b21dbd8890
@ -11,7 +11,8 @@ from pathlib import Path
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
# ========== Constants ==========
|
# ========== Constants ==========
|
||||||
VALID_DB_COMPRESS_MODES = ["bzip2", "gz", "lzma", "xz"]
|
LOCKFILE_MODE = 0o0000
|
||||||
|
VALID_DB_COMPRESS_MODES = [None, "bzip2", "gz", "lzma", "xz"]
|
||||||
|
|
||||||
# ========== Logging Setup ==========
|
# ========== Logging Setup ==========
|
||||||
syslog = logging.getLogger(__name__)
|
syslog = logging.getLogger(__name__)
|
||||||
@ -19,20 +20,62 @@ syslog = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# ========== Classes ==========
|
# ========== Classes ==========
|
||||||
class PackageManager:
|
class PackageManager:
|
||||||
def __init__(self, cachedir, db_path, pkglist_cmd):
|
"""Class for abstracting package manager-based operations.
|
||||||
|
|
||||||
|
The package manager can be used in conjunction with a ``Snapshot`` for backups.
|
||||||
|
|
||||||
|
Lockfile Management
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This class can be used as a context manager for creating a lockfile for the
|
||||||
|
specific package manager. This is to prevent transactions from occurring during
|
||||||
|
backup operations which would most likely leave the package manager's database in
|
||||||
|
an inconsistent state on the backup.
|
||||||
|
|
||||||
|
.. note:: Subclasses can override the context manager and implement i.e. blocking until
|
||||||
|
the process is complete with a timeout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cachedir=None, db_path=None, lockfile=None, pkglist_cmd=None):
|
||||||
"""Default constructor for the PackageManager class.
|
"""Default constructor for the PackageManager class.
|
||||||
|
|
||||||
:param cachedir: path to the package manager cache directory
|
:param cachedir: path to the package manager cache directory
|
||||||
:type cachedir: str or path-like object
|
:type cachedir: str or path-like object
|
||||||
:param db_path: path to the package manager database
|
:param db_path: path to the package manager database
|
||||||
:type db_path: str or path-like object
|
:type db_path: str or path-like object
|
||||||
|
:param lockfile: path to this package manager's lockfile
|
||||||
|
:type lockfile: str
|
||||||
:param pkglist_cmd: command to list installed packages to stdout
|
:param pkglist_cmd: command to list installed packages to stdout
|
||||||
:type pkglist_cmd: str or iterable of str
|
:type pkglist_cmd: str or iterable of str
|
||||||
"""
|
"""
|
||||||
self._cachedir = Path(cachedir)
|
self._cachedir = Path(cachedir)
|
||||||
self._db_path = Path(db_path)
|
self._db_path = Path(db_path)
|
||||||
|
self._lockfile = Path(lockfile)
|
||||||
self._pkglist_cmd = pkglist_cmd
|
self._pkglist_cmd = pkglist_cmd
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
"""Create the package manager's lockfile. This prevents transactions
|
||||||
|
from occurring during the backup which could leave the database backup
|
||||||
|
in an inconsistent state.
|
||||||
|
|
||||||
|
The existence of this lockfile is an error, and its meaning is up to
|
||||||
|
the package manager. For example, pacman's db.lck indicates
|
||||||
|
either there is an ongoing transaction in progress or a previous transaction
|
||||||
|
failed and the database is in an inconsistent state.
|
||||||
|
|
||||||
|
:returns: self
|
||||||
|
:rtype: ``PackageManager`` object
|
||||||
|
:raises FileExistsError: if lockfile exists when this method is called
|
||||||
|
"""
|
||||||
|
self._lockfile.touch(mode=0o000)
|
||||||
|
yield self
|
||||||
|
|
||||||
|
def __exit__(self):
|
||||||
|
"""Remove the package manager's lockfile. After this lockfile is closed,
|
||||||
|
the package manager this class abstracts can perform transactions once again.
|
||||||
|
"""
|
||||||
|
self._lockfile.unlink()
|
||||||
|
|
||||||
def gen_pkglist(self):
|
def gen_pkglist(self):
|
||||||
"""Generate a text file listing installed packages
|
"""Generate a text file listing installed packages
|
||||||
on the system and return the path to that file.
|
on the system and return the path to that file.
|
||||||
@ -109,6 +152,14 @@ class PackageManager:
|
|||||||
"""
|
"""
|
||||||
return self._db_path
|
return self._db_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lockfile(self):
|
||||||
|
"""
|
||||||
|
:returns: the lockfile path of this package manager
|
||||||
|
:rtype: path-like object
|
||||||
|
"""
|
||||||
|
return self._lockfile
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pkglist_cmd(self):
|
def pkglist_cmd(self):
|
||||||
"""
|
"""
|
||||||
|
@ -60,8 +60,11 @@ class TestPackageManagerMethods(unittest.TestCase):
|
|||||||
|
|
||||||
self.cachedir = "/var/cache/pacman/"
|
self.cachedir = "/var/cache/pacman/"
|
||||||
self.db_path = "/var/lib/pacman"
|
self.db_path = "/var/lib/pacman"
|
||||||
|
self.lockfile = "/var/lib/pacman/db.lck"
|
||||||
self.pkglist_cmd = ["pacman", "-Qqe"]
|
self.pkglist_cmd = ["pacman", "-Qqe"]
|
||||||
self.p = PackageManager(self.cachedir, self.db_path, self.pkglist_cmd)
|
self.p = PackageManager(
|
||||||
|
self.cachedir, self.db_path, self.lockfile, self.pkglist_cmd
|
||||||
|
)
|
||||||
|
|
||||||
def test_pkglist(self):
|
def test_pkglist(self):
|
||||||
self.mocked_run.return_value.stdout = "packages"
|
self.mocked_run.return_value.stdout = "packages"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user