borg with-lock REPO CMD ARGS

This commit is contained in:
Thomas Waldmann 2016-04-28 01:28:43 +02:00
parent 12fb137667
commit 962c2e9d54
4 changed files with 83 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import os
import shlex
import signal
import stat
import subprocess
import sys
import textwrap
import traceback
@ -895,6 +896,21 @@ class Archiver:
cache.commit()
return self.exit_code
@with_repository(manifest=False)
def do_with_lock(self, args, repository):
"""run a user specified command with the repository lock held"""
# re-write manifest to start a repository transaction - this causes a
# lock upgrade to exclusive for remote (and also for local) repositories.
# by using manifest=False in the decorator, we avoid having to require
# the encryption key (and can operate just with encrypted data).
data = repository.get(Manifest.MANIFEST_ID)
repository.put(Manifest.MANIFEST_ID, data)
try:
# we exit with the return code we get from the subprocess
return subprocess.call([args.command] + args.args)
finally:
repository.rollback()
@with_repository()
def do_debug_dump_archive_items(self, args, repository, manifest, key):
"""dump (decrypted, decompressed) archive items metadata (not: data)"""
@ -1831,6 +1847,32 @@ class Archiver:
subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
help='paths to recreate; patterns are supported')
with_lock_epilog = textwrap.dedent("""
This command runs a user-specified command while the repository lock is held.
It will first try to acquire the lock (make sure that no other operation is
running in the repo), then execute the given command as a subprocess and wait
for its termination, release the lock and return the user command's return
code as borg's return code.
Note: if you copy a repository with the lock held, the lock will be present in
the copy, obviously. Thus, before using borg on the copy, you need to
use "borg break-lock" on it.
""")
subparser = subparsers.add_parser('with-lock', parents=[common_parser], add_help=False,
description=self.do_with_lock.__doc__,
epilog=with_lock_epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
help='run user command with lock held')
subparser.set_defaults(func=self.do_with_lock)
subparser.add_argument('location', metavar='REPOSITORY',
type=location_validator(archive=False),
help='repository to lock')
subparser.add_argument('command', metavar='COMMAND',
help='command to run')
subparser.add_argument('args', metavar='ARGS', nargs=argparse.REMAINDER,
help='command arguments')
subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
description='Extra help')
subparser.add_argument('--epilog-only', dest='epilog_only',

View File

@ -1399,6 +1399,12 @@ class ArchiverTestCase(ArchiverTestCaseBase):
info_after = self.cmd('info', self.repository_location + '::test')
assert info_before == info_after # includes archive ID
def test_with_lock(self):
self.cmd('init', self.repository_location)
lock_path = os.path.join(self.repository_path, 'lock.exclusive')
cmd = 'python3', '-c', 'import os, sys; sys.exit(42 if os.path.exists("%s") else 23)' % lock_path
self.cmd('with-lock', self.repository_location, *cmd, fork=True, exit_code=42)
@unittest.skipUnless('binary' in BORG_EXES, 'no borg.exe available')
class ArchiverTestCaseBinary(ArchiverTestCase):

View File

@ -647,6 +647,9 @@ Examples
...
.. include:: usage/with-lock.rst.inc
.. include:: usage/break-lock.rst.inc

View File

@ -0,0 +1,32 @@
.. _borg_with-lock:
borg with-lock
--------------
::
borg with-lock <options> REPOSITORY COMMAND ARGS
positional arguments
REPOSITORY
repository to lock
COMMAND
command to run
ARGS
command arguments
`Common options`_
|
Description
~~~~~~~~~~~
This command runs a user-specified command while the repository lock is held.
It will first try to acquire the lock (make sure that no other operation is
running in the repo), then execute the given command as a subprocess and wait
for its termination, release the lock and return the user command's return
code as borg's return code.
Note: if you copy a repository with the lock held, the lock will be present in
the copy, obviously. Thus, before using borg on the copy, you need to
use "borg break-lock" on it.