mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-14 20:13:21 -04:00
Merge pull request #6766 from ThomasWaldmann/split-repo-archive
borg2: split repo and archive name into separate args, fixes #948
This commit is contained in:
commit
d039f2685a
9 changed files with 1283 additions and 1468 deletions
|
|
@ -280,7 +280,7 @@ class build_man(Command):
|
|||
'recreate': ('patterns', 'placeholders', 'compression'),
|
||||
'list': ('info', 'diff', 'prune', 'patterns'),
|
||||
'info': ('list', 'diff'),
|
||||
'init': ('create', 'delete', 'check', 'list', 'key-import', 'key-export', 'key-change-passphrase'),
|
||||
'rcreate': ('rdelete', 'rlist', 'check', 'key-import', 'key-export', 'key-change-passphrase'),
|
||||
'key-import': ('key-export', ),
|
||||
'key-export': ('key-import', ),
|
||||
'mount': ('umount', 'extract'), # Would be cooler if these two were on the same page
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -35,7 +35,7 @@ from .crypto.low_level import blake2b_128
|
|||
from .archiver import Archiver
|
||||
from .archive import Archive, get_item_uid_gid
|
||||
from .hashindex import FuseVersionsIndex
|
||||
from .helpers import daemonize, daemonizing, signal_handler, format_file_size, Error
|
||||
from .helpers import daemonize, daemonizing, signal_handler, format_file_size
|
||||
from .helpers import HardLinkManager
|
||||
from .helpers import msgpack
|
||||
from .item import Item
|
||||
|
|
@ -272,22 +272,16 @@ class FuseBackend:
|
|||
|
||||
def _create_filesystem(self):
|
||||
self._create_dir(parent=1) # first call, create root dir (inode == 1)
|
||||
if self._args.location.archive:
|
||||
self.versions_index = FuseVersionsIndex()
|
||||
for archive in self._manifest.archives.list_considering(self._args):
|
||||
if self.versions:
|
||||
raise Error("for versions view, do not specify a single archive, "
|
||||
"but always give the repository as location.")
|
||||
self._process_archive(self._args.location.archive)
|
||||
else:
|
||||
self.versions_index = FuseVersionsIndex()
|
||||
for archive in self._manifest.archives.list_considering(self._args):
|
||||
if self.versions:
|
||||
# process archives immediately
|
||||
self._process_archive(archive.name)
|
||||
else:
|
||||
# lazily load archives, create archive placeholder inode
|
||||
archive_inode = self._create_dir(parent=1, mtime=int(archive.ts.timestamp() * 1e9))
|
||||
self.contents[1][os.fsencode(archive.name)] = archive_inode
|
||||
self.pending_archives[archive_inode] = archive.name
|
||||
# process archives immediately
|
||||
self._process_archive(archive.name)
|
||||
else:
|
||||
# lazily load archives, create archive placeholder inode
|
||||
archive_inode = self._create_dir(parent=1, mtime=int(archive.ts.timestamp() * 1e9))
|
||||
self.contents[1][os.fsencode(archive.name)] = archive_inode
|
||||
self.pending_archives[archive_inode] = archive.name
|
||||
|
||||
def get_item(self, inode):
|
||||
item = self._inode_cache.get(inode)
|
||||
|
|
|
|||
|
|
@ -103,11 +103,13 @@ class Archives(abc.MutableMapping):
|
|||
"""
|
||||
get a list of archives, considering --first/last/prefix/glob-archives/sort/consider-checkpoints cmdline args
|
||||
"""
|
||||
if args.location.archive:
|
||||
raise Error('The options --first, --last, --prefix, and --glob-archives, and --consider-checkpoints can only be used on repository targets.')
|
||||
name = getattr(args, 'name', None)
|
||||
consider_checkpoints = getattr(args, 'consider_checkpoints', None)
|
||||
if name is not None:
|
||||
raise Error('Giving a specific name is incompatible with options --first, --last, --prefix, and --glob-archives, and --consider-checkpoints.')
|
||||
if args.prefix is not None:
|
||||
args.glob_archives = args.prefix + '*'
|
||||
return self.list(sort_by=args.sort_by.split(','), consider_checkpoints=args.consider_checkpoints, glob=args.glob_archives, first=args.first, last=args.last)
|
||||
return self.list(sort_by=args.sort_by.split(','), consider_checkpoints=consider_checkpoints, glob=args.glob_archives, first=args.first, last=args.last)
|
||||
|
||||
def set_raw_dict(self, d):
|
||||
"""set the dict we get from the msgpack unpacker"""
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ PrefixSpec = replace_placeholders
|
|||
|
||||
GlobSpec = replace_placeholders
|
||||
|
||||
NameSpec = replace_placeholders
|
||||
|
||||
CommentSpec = replace_placeholders
|
||||
|
||||
|
||||
|
|
@ -299,9 +301,7 @@ def parse_stringified_list(s):
|
|||
|
||||
|
||||
class Location:
|
||||
"""Object representing a repository / archive location
|
||||
"""
|
||||
proto = user = _host = port = path = archive = None
|
||||
"""Object representing a repository location"""
|
||||
|
||||
# user must not contain "@", ":" or "/".
|
||||
# Quoting adduser error message:
|
||||
|
|
@ -333,15 +333,6 @@ class Location:
|
|||
(?P<path>(/([^:]|(:(?!:)))+)) # start with /, then any chars, but no "::"
|
||||
"""
|
||||
|
||||
# optional ::archive_name at the end, archive name must not contain "/".
|
||||
# borg mount's FUSE filesystem creates one level of directories from
|
||||
# the archive names and of course "/" is not valid in a directory name.
|
||||
optional_archive_re = r"""
|
||||
(?:
|
||||
:: # "::" as separator
|
||||
(?P<archive>[^/]+) # archive name must not contain "/"
|
||||
)?$""" # must match until the end
|
||||
|
||||
# host NAME, or host IP ADDRESS (v4 or v6, v6 must be in square brackets)
|
||||
host_re = r"""
|
||||
(?P<host>(
|
||||
|
|
@ -356,23 +347,13 @@ class Location:
|
|||
(?P<proto>ssh):// # ssh://
|
||||
""" + optional_user_re + host_re + r""" # user@ (optional), host name or address
|
||||
(?::(?P<port>\d+))? # :port (optional)
|
||||
""" + abs_path_re + optional_archive_re, re.VERBOSE) # path or path::archive
|
||||
""" + abs_path_re, re.VERBOSE) # path
|
||||
|
||||
file_re = re.compile(r"""
|
||||
(?P<proto>file):// # file://
|
||||
""" + file_path_re + optional_archive_re, re.VERBOSE) # servername/path, path or path::archive
|
||||
""" + file_path_re, re.VERBOSE) # servername/path or path
|
||||
|
||||
local_re = re.compile(
|
||||
local_path_re + optional_archive_re, re.VERBOSE) # local path with optional archive
|
||||
|
||||
# get the repo from BORG_REPO env and the optional archive from param.
|
||||
# if the syntax requires giving REPOSITORY (see "borg mount"),
|
||||
# use "::" to let it use the env var.
|
||||
# if REPOSITORY argument is optional, it'll automatically use the env.
|
||||
env_re = re.compile(r""" # the repo part is fetched from BORG_REPO
|
||||
(?:::$) # just "::" is ok (when a pos. arg is required, no archive)
|
||||
| # or
|
||||
""" + optional_archive_re, re.VERBOSE) # archive name (optional, may be empty)
|
||||
local_re = re.compile(local_path_re, re.VERBOSE) # local path
|
||||
|
||||
win_file_re = re.compile(r"""
|
||||
(?:file://)? # optional file protocol
|
||||
|
|
@ -380,31 +361,34 @@ class Location:
|
|||
(?:[a-zA-Z]:)? # Drive letter followed by a colon (optional)
|
||||
(?:[^:]+) # Anything which does not contain a :, at least one character
|
||||
)
|
||||
""" + optional_archive_re, re.VERBOSE) # archive name (optional, may be empty)
|
||||
""", re.VERBOSE)
|
||||
|
||||
def __init__(self, text='', overrides={}, other=False):
|
||||
self.repo_env_var = 'BORG_OTHER_REPO' if other else 'BORG_REPO'
|
||||
if not self.parse(text, overrides):
|
||||
raise ValueError('Invalid location format: "%s"' % self.processed)
|
||||
self.valid = False
|
||||
self.proto = None
|
||||
self.user = None
|
||||
self._host = None
|
||||
self.port = None
|
||||
self.path = None
|
||||
self.raw = None
|
||||
self.processed = None
|
||||
self.parse(text, overrides)
|
||||
|
||||
def parse(self, text, overrides={}):
|
||||
if not text:
|
||||
# we did not get a text to parse, so we try to fetch from the environment
|
||||
text = os.environ.get(self.repo_env_var)
|
||||
if text is None:
|
||||
return
|
||||
|
||||
self.raw = text # as given by user, might contain placeholders
|
||||
self.processed = text = replace_placeholders(text, overrides) # after placeholder replacement
|
||||
valid = self._parse(text)
|
||||
self.processed = replace_placeholders(self.raw, overrides) # after placeholder replacement
|
||||
valid = self._parse(self.processed)
|
||||
if valid:
|
||||
return True
|
||||
m = self.env_re.match(text)
|
||||
if not m:
|
||||
return False
|
||||
repo_raw = os.environ.get(self.repo_env_var)
|
||||
if repo_raw is None:
|
||||
return False
|
||||
repo = replace_placeholders(repo_raw, overrides)
|
||||
valid = self._parse(repo)
|
||||
self.archive = m.group('archive')
|
||||
self.raw = repo_raw if not self.archive else repo_raw + self.raw
|
||||
self.processed = repo if not self.archive else f'{repo}::{self.archive}'
|
||||
return valid
|
||||
self.valid = True
|
||||
else:
|
||||
raise ValueError('Invalid location format: "%s"' % self.processed)
|
||||
|
||||
def _parse(self, text):
|
||||
def normpath_special(p):
|
||||
|
|
@ -418,10 +402,9 @@ class Location:
|
|||
if m:
|
||||
self.proto = 'file'
|
||||
self.path = m.group('path')
|
||||
self.archive = m.group('archive')
|
||||
return True
|
||||
|
||||
# On windows we currently only support windows paths
|
||||
# On windows we currently only support windows paths.
|
||||
return False
|
||||
|
||||
m = self.ssh_re.match(text)
|
||||
|
|
@ -431,19 +414,16 @@ class Location:
|
|||
self._host = m.group('host')
|
||||
self.port = m.group('port') and int(m.group('port')) or None
|
||||
self.path = normpath_special(m.group('path'))
|
||||
self.archive = m.group('archive')
|
||||
return True
|
||||
m = self.file_re.match(text)
|
||||
if m:
|
||||
self.proto = m.group('proto')
|
||||
self.path = normpath_special(m.group('path'))
|
||||
self.archive = m.group('archive')
|
||||
return True
|
||||
m = self.local_re.match(text)
|
||||
if m:
|
||||
self.path = normpath_special(m.group('path'))
|
||||
self.archive = m.group('archive')
|
||||
self.proto = 'file'
|
||||
self.path = normpath_special(m.group('path'))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -454,7 +434,6 @@ class Location:
|
|||
'host=%r' % self.host,
|
||||
'port=%r' % self.port,
|
||||
'path=%r' % self.path,
|
||||
'archive=%r' % self.archive,
|
||||
]
|
||||
return ', '.join(items)
|
||||
|
||||
|
|
@ -499,24 +478,13 @@ class Location:
|
|||
'utcnow': DatetimeWrapper(timestamp),
|
||||
})
|
||||
|
||||
def omit_archive(self):
|
||||
loc = Location(self.raw)
|
||||
loc.archive = None
|
||||
loc.raw = loc.raw.split("::")[0]
|
||||
loc.processed = loc.processed.split("::")[0]
|
||||
return loc
|
||||
|
||||
|
||||
def location_validator(archive=None, proto=None, other=False):
|
||||
def location_validator(proto=None, other=False):
|
||||
def validator(text):
|
||||
try:
|
||||
loc = Location(text, other=other)
|
||||
except ValueError as err:
|
||||
raise argparse.ArgumentTypeError(str(err)) from None
|
||||
if archive is True and not loc.archive:
|
||||
raise argparse.ArgumentTypeError('"%s": No archive specified' % text)
|
||||
elif archive is False and loc.archive:
|
||||
raise argparse.ArgumentTypeError('"%s": No archive can be specified' % text)
|
||||
if proto is not None and loc.proto != proto:
|
||||
if proto == 'file':
|
||||
raise argparse.ArgumentTypeError('"%s": Repository must be local' % text)
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class BaseTestCase(unittest.TestCase):
|
|||
mountpoint = tempfile.mkdtemp()
|
||||
else:
|
||||
os.mkdir(mountpoint)
|
||||
args = ['mount', location, mountpoint] + list(options)
|
||||
args = [f'--repo={location}', 'mount', mountpoint] + list(options)
|
||||
if os_fork:
|
||||
# Do not spawn, but actually (OS) fork.
|
||||
if os.fork() == 0:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -28,7 +28,7 @@ def repo_url(request, tmpdir, monkeypatch):
|
|||
|
||||
@pytest.fixture(params=["none", "repokey"])
|
||||
def repo(request, cmd, repo_url):
|
||||
cmd('init', '--encryption', request.param, repo_url)
|
||||
cmd(f'--repo={repo_url}', 'rcreate', '--encryption', request.param)
|
||||
return repo_url
|
||||
|
||||
|
||||
|
|
@ -55,46 +55,52 @@ def testdata(request, tmpdir_factory):
|
|||
|
||||
|
||||
@pytest.fixture(params=['none', 'lz4'])
|
||||
def archive(request, cmd, repo, testdata):
|
||||
archive_url = repo + '::test'
|
||||
cmd('create', '--compression', request.param, archive_url, testdata)
|
||||
return archive_url
|
||||
def repo_archive(request, cmd, repo, testdata):
|
||||
archive = 'test'
|
||||
cmd(f'--repo={repo}', 'create', '--compression', request.param, archive, testdata)
|
||||
return repo, archive
|
||||
|
||||
|
||||
def test_create_none(benchmark, cmd, repo, testdata):
|
||||
result, out = benchmark.pedantic(cmd, ('create', '--compression', 'none', repo + '::test', testdata))
|
||||
result, out = benchmark.pedantic(cmd, (f'--repo={repo}', 'create', '--compression', 'none',
|
||||
'test', testdata))
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_create_lz4(benchmark, cmd, repo, testdata):
|
||||
result, out = benchmark.pedantic(cmd, ('create', '--compression', 'lz4', repo + '::test', testdata))
|
||||
result, out = benchmark.pedantic(cmd, (f'--repo={repo}', 'create', '--compression', 'lz4',
|
||||
'test', testdata))
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_extract(benchmark, cmd, archive, tmpdir):
|
||||
def test_extract(benchmark, cmd, repo_archive, tmpdir):
|
||||
repo, archive = repo_archive
|
||||
with changedir(str(tmpdir)):
|
||||
result, out = benchmark.pedantic(cmd, ('extract', archive))
|
||||
result, out = benchmark.pedantic(cmd, (f'--repo={repo}', 'extract', archive))
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_delete(benchmark, cmd, archive):
|
||||
result, out = benchmark.pedantic(cmd, ('delete', archive))
|
||||
def test_delete(benchmark, cmd, repo_archive):
|
||||
repo, archive = repo_archive
|
||||
result, out = benchmark.pedantic(cmd, (f'--repo={repo}', 'delete', '-a', archive))
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_list(benchmark, cmd, archive):
|
||||
result, out = benchmark(cmd, 'list', archive)
|
||||
def test_list(benchmark, cmd, repo_archive):
|
||||
repo, archive = repo_archive
|
||||
result, out = benchmark(cmd, f'--repo={repo}', 'list', archive)
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_info(benchmark, cmd, archive):
|
||||
result, out = benchmark(cmd, 'info', archive)
|
||||
def test_info(benchmark, cmd, repo_archive):
|
||||
repo, archive = repo_archive
|
||||
result, out = benchmark(cmd, f'--repo={repo}', 'info', '-a', archive)
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_check(benchmark, cmd, archive):
|
||||
repo = archive.split('::')[0]
|
||||
result, out = benchmark(cmd, 'check', repo)
|
||||
def test_check(benchmark, cmd, repo_archive):
|
||||
repo, archive = repo_archive
|
||||
result, out = benchmark(cmd, f'--repo={repo}', 'check')
|
||||
assert result == 0
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -54,65 +54,63 @@ class TestLocationWithoutEnv:
|
|||
|
||||
def test_ssh(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('ssh://user@host:1234/some/path::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive='archive')"
|
||||
assert Location('ssh://user@host:1234/some/path::archive').to_key_filename() == keys_dir + 'host__some_path'
|
||||
assert repr(Location('ssh://user@host:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path')"
|
||||
assert Location('ssh://user@host:1234/some/path').to_key_filename() == keys_dir + 'host__some_path'
|
||||
assert repr(Location('ssh://user@host:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@host/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
|
||||
assert repr(Location('ssh://user@[::]:1234/some/path::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive='archive')"
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[::]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[::]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path')"
|
||||
assert Location('ssh://user@[::]:1234/some/path').to_key_filename() == keys_dir + '____some_path'
|
||||
assert repr(Location('ssh://user@[::]/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
|
||||
assert repr(Location('ssh://user@[2001:db8::]:1234/some/path::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive='archive')"
|
||||
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path')"
|
||||
assert Location('ssh://user@[2001:db8::]:1234/some/path').to_key_filename() == keys_dir + '2001_db8____some_path'
|
||||
assert repr(Location('ssh://user@[2001:db8::]/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
|
||||
assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive='archive')"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::c0:ffee]/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
|
||||
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive='archive')"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path')"
|
||||
assert Location('ssh://user@[2001:db8::192.0.2.1]/some/path').to_key_filename() == keys_dir + '2001_db8__192_0_2_1__some_path'
|
||||
assert repr(Location('ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='/some/path')"
|
||||
assert repr(Location('ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]:1234/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='/some/path')"
|
||||
|
||||
def test_file(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('file:///some/path::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive='archive')"
|
||||
assert repr(Location('file:///some/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path')"
|
||||
assert repr(Location('file:///some/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path')"
|
||||
assert Location('file:///some/path').to_key_filename() == keys_dir + 'some_path'
|
||||
|
||||
def test_smb(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('file:////server/share/path::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='//server/share/path', archive='archive')"
|
||||
assert Location('file:////server/share/path::archive').to_key_filename() == keys_dir + 'server_share_path'
|
||||
assert repr(Location('file:////server/share/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='//server/share/path')"
|
||||
assert Location('file:////server/share/path').to_key_filename() == keys_dir + 'server_share_path'
|
||||
|
||||
def test_folder(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('path::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive')"
|
||||
assert repr(Location('path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive=None)"
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path')"
|
||||
assert Location('path').to_key_filename() == keys_dir + 'path'
|
||||
|
||||
def test_long_path(self, monkeypatch, keys_dir):
|
||||
|
|
@ -121,168 +119,61 @@ class TestLocationWithoutEnv:
|
|||
|
||||
def test_abspath(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('/some/absolute/path::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive='archive')"
|
||||
assert repr(Location('/some/absolute/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)"
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path')"
|
||||
assert repr(Location('/some/absolute/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path')"
|
||||
assert Location('/some/absolute/path').to_key_filename() == keys_dir + 'some_absolute_path'
|
||||
assert repr(Location('ssh://user@host/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path')"
|
||||
assert Location('ssh://user@host/some/path').to_key_filename() == keys_dir + 'host__some_path'
|
||||
|
||||
def test_relpath(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('some/relative/path::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive='archive')"
|
||||
assert repr(Location('some/relative/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)"
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path')"
|
||||
assert repr(Location('some/relative/path')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path')"
|
||||
assert Location('some/relative/path').to_key_filename() == keys_dir + 'some_relative_path'
|
||||
assert repr(Location('ssh://user@host/./some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/./some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/./some/path')"
|
||||
assert Location('ssh://user@host/./some/path').to_key_filename() == keys_dir + 'host__some_path'
|
||||
assert repr(Location('ssh://user@host/~/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/~/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/~/some/path')"
|
||||
assert Location('ssh://user@host/~/some/path').to_key_filename() == keys_dir + 'host__some_path'
|
||||
assert repr(Location('ssh://user@host/~user/some/path')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/~user/some/path', archive=None)"
|
||||
"Location(proto='ssh', user='user', host='host', port=None, path='/~user/some/path')"
|
||||
assert Location('ssh://user@host/~user/some/path').to_key_filename() == keys_dir + 'host__user_some_path'
|
||||
|
||||
def test_with_colons(self, monkeypatch, keys_dir):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
assert repr(Location('/abs/path:w:cols::arch:col')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive='arch:col')"
|
||||
assert repr(Location('/abs/path:with:colons::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons', archive='archive')"
|
||||
assert repr(Location('/abs/path:w:cols')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols')"
|
||||
assert repr(Location('/abs/path:with:colons')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons', archive=None)"
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons')"
|
||||
assert repr(Location('/abs/path:with:colons')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons')"
|
||||
assert Location('/abs/path:with:colons').to_key_filename() == keys_dir + 'abs_path_with_colons'
|
||||
|
||||
def test_user_parsing(self):
|
||||
# see issue #1930
|
||||
assert repr(Location('ssh://host/path::2016-12-31@23:59:59')) == \
|
||||
"Location(proto='ssh', user=None, host='host', port=None, path='/path', archive='2016-12-31@23:59:59')"
|
||||
|
||||
def test_with_timestamp(self):
|
||||
assert repr(Location('path::archive-{utcnow}').with_timestamp(datetime(2002, 9, 19, tzinfo=timezone.utc))) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive-2002-09-19T00:00:00')"
|
||||
|
||||
def test_underspecified(self, monkeypatch):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
with pytest.raises(ValueError):
|
||||
Location('::archive')
|
||||
with pytest.raises(ValueError):
|
||||
Location('::')
|
||||
with pytest.raises(ValueError):
|
||||
Location()
|
||||
|
||||
def test_no_slashes(self, monkeypatch):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
with pytest.raises(ValueError):
|
||||
Location('/some/path/to/repo::archive_name_with/slashes/is_invalid')
|
||||
assert repr(Location('ssh://host/path')) == \
|
||||
"Location(proto='ssh', user=None, host='host', port=None, path='/path')"
|
||||
|
||||
def test_canonical_path(self, monkeypatch):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
locations = ['some/path::archive', 'file://some/path::archive', 'host:some/path::archive',
|
||||
'host:~user/some/path::archive', 'ssh://host/some/path::archive',
|
||||
'ssh://user@host:1234/some/path::archive']
|
||||
locations = ['some/path', 'file://some/path', 'host:some/path',
|
||||
'host:~user/some/path', 'ssh://host/some/path',
|
||||
'ssh://user@host:1234/some/path']
|
||||
for location in locations:
|
||||
assert Location(location).canonical_path() == \
|
||||
Location(Location(location).canonical_path()).canonical_path(), "failed: %s" % location
|
||||
|
||||
def test_format_path(self, monkeypatch):
|
||||
monkeypatch.delenv('BORG_REPO', raising=False)
|
||||
test_pid = os.getpid()
|
||||
assert repr(Location('/some/path::archive{pid}')) == \
|
||||
f"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive='archive{test_pid}')"
|
||||
location_time1 = Location('/some/path::archive{now:%s}')
|
||||
sleep(1.1)
|
||||
location_time2 = Location('/some/path::archive{now:%s}')
|
||||
assert location_time1.archive != location_time2.archive
|
||||
|
||||
def test_bad_syntax(self):
|
||||
with pytest.raises(ValueError):
|
||||
# this is invalid due to the 2nd colon, correct: 'ssh://user@host/path'
|
||||
Location('ssh://user@host:/path')
|
||||
|
||||
def test_omit_archive(self):
|
||||
from borg.platform import hostname
|
||||
loc = Location('ssh://user@host:1234/repos/{hostname}::archive')
|
||||
loc_without_archive = loc.omit_archive()
|
||||
assert loc_without_archive.archive is None
|
||||
assert loc_without_archive.raw == "ssh://user@host:1234/repos/{hostname}"
|
||||
assert loc_without_archive.processed == "ssh://user@host:1234/repos/%s" % hostname
|
||||
|
||||
|
||||
class TestLocationWithEnv:
|
||||
def test_ssh(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', 'ssh://user@host:1234/some/path')
|
||||
assert repr(Location('::archive')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
|
||||
|
||||
def test_ssh_placeholder(self, monkeypatch):
|
||||
from borg.platform import hostname
|
||||
monkeypatch.setenv('BORG_REPO', 'ssh://user@host:1234/{hostname}')
|
||||
assert repr(Location('::archive')) == \
|
||||
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive=None)"
|
||||
|
||||
def test_file(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', 'file:///some/path')
|
||||
assert repr(Location('::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
|
||||
|
||||
def test_folder(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', 'path')
|
||||
assert repr(Location('::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='path', archive=None)"
|
||||
|
||||
def test_abspath(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', '/some/absolute/path')
|
||||
assert repr(Location('::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)"
|
||||
|
||||
def test_relpath(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', 'some/relative/path')
|
||||
assert repr(Location('::archive')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive='archive')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)"
|
||||
|
||||
def test_with_colons(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', '/abs/path:w:cols')
|
||||
assert repr(Location('::arch:col')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive='arch:col')"
|
||||
assert repr(Location('::')) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive=None)"
|
||||
assert repr(Location()) == \
|
||||
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive=None)"
|
||||
|
||||
def test_no_slashes(self, monkeypatch):
|
||||
monkeypatch.setenv('BORG_REPO', '/some/absolute/path')
|
||||
with pytest.raises(ValueError):
|
||||
Location('::archive_name_with/slashes/is_invalid')
|
||||
|
||||
|
||||
class FormatTimedeltaTestCase(BaseTestCase):
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue