1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-03-07 03:59:19 +00:00

new ssh: URLs, see

ssh://user@host:port/rel/path
ssh://user@host:port//abs/path

remove the /./ and /~/ hacks.
This commit is contained in:
Thomas Waldmann 2024-10-14 19:51:44 +02:00
parent 72fc028142
commit 835b2657cd
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
6 changed files with 74 additions and 124 deletions

View file

@ -120,7 +120,7 @@ def archiver(tmp_path, set_env_variables):
@pytest.fixture()
def remote_archiver(archiver):
archiver.repository_location = "ssh://__testsuite__" + str(archiver.repository_path)
archiver.repository_location = "ssh://__testsuite__/" + str(archiver.repository_path)
yield archiver

View file

@ -454,9 +454,9 @@ class Location:
+ optional_user_re
+ host_re
+ r""" # user@ (optional), host name or address
(?::(?P<port>\d+))? # :port (optional)
(?::(?P<port>\d+))?/ # :port (optional) + "/" as separator
"""
+ abs_path_re,
+ path_re,
re.VERBOSE,
) # path
@ -538,19 +538,13 @@ class Location:
raise ValueError('Invalid location format: "%s"' % self.processed)
def _parse(self, text):
def normpath_special(p):
# avoid that normpath strips away our relative path hack and even makes p absolute
relative = p.startswith("/./")
p = os.path.normpath(p)
return ("/." + p) if relative else p
m = self.ssh_re.match(text)
if m:
self.proto = m.group("proto")
self.user = m.group("user")
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.path = os.path.normpath(m.group("path"))
return True
m = self.sftp_re.match(text)
if m:
@ -558,7 +552,7 @@ class Location:
self.user = m.group("user")
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.path = os.path.normpath(m.group("path"))
return True
m = self.rclone_re.match(text)
if m:
@ -568,17 +562,17 @@ class Location:
m = self.file_re.match(text)
if m:
self.proto = m.group("proto")
self.path = normpath_special(m.group("path"))
self.path = os.path.normpath(m.group("path"))
return True
m = self.socket_re.match(text)
if m:
self.proto = m.group("proto")
self.path = normpath_special(m.group("path"))
self.path = os.path.normpath(m.group("path"))
return True
m = self.local_re.match(text)
if m:
self.proto = "file"
self.path = normpath_special(m.group("path"))
self.path = os.path.normpath(m.group("path"))
return True
return False
@ -615,31 +609,17 @@ class Location:
def canonical_path(self):
if self.proto in ("file", "socket"):
return self.path
else:
if self.path and self.path.startswith("~"):
path = "/" + self.path # /~/x = path x relative to home dir
elif self.path and not self.path.startswith("/"):
path = "/./" + self.path # /./x = path x relative to cwd
else:
path = self.path
if self.proto == "rclone":
return f"{self.proto}:{self.path}"
elif self.proto == "sftp":
return (
f"{self.proto}://"
f"{(self.user + '@') if self.user else ''}"
f"{self._host if self._host else ''}"
f"{self.port if self.port else ''}/"
f"{path}"
)
else:
return "{}://{}{}{}{}".format(
self.proto if self.proto else "???",
f"{self.user}@" if self.user else "",
self._host if self._host else "", # needed for ipv6 addrs
f":{self.port}" if self.port else "",
path,
)
if self.proto == "rclone":
return f"{self.proto}:{self.path}"
if self.proto in ("sftp", "ssh"):
return (
f"{self.proto}://"
f"{(self.user + '@') if self.user else ''}"
f"{self._host if self._host else ''}"
f"{self.port if self.port else ''}/"
f"{self.path}"
)
raise NotImplementedError(self.proto)
def with_timestamp(self, timestamp):
# note: this only affects the repository URL/path, not the archive name!

View file

@ -361,12 +361,8 @@ class RepositoryServer: # pragma: no cover
def _resolve_path(self, path):
if isinstance(path, bytes):
path = os.fsdecode(path)
if path.startswith("/~/"): # /~/x = path x relative to own home dir
home_dir = os.environ.get("HOME") or os.path.expanduser("~%s" % os.environ.get("USER", ""))
path = os.path.join(home_dir, path[3:])
elif path.startswith("/./"): # /./x = path x relative to cwd
path = path[3:]
return os.path.realpath(path)
path = os.path.realpath(path)
return path
def open(
self,

View file

@ -108,83 +108,69 @@ class TestLocationWithoutEnv:
def test_ssh(self, monkeypatch, keys_dir):
monkeypatch.delenv("BORG_REPO", raising=False)
assert (
repr(Location("ssh://user@host:1234/some/path"))
== "Location(proto='ssh', user='user', host='host', port=1234, path='/some/path')"
repr(Location("ssh://user@host:1234//absolute/path"))
== "Location(proto='ssh', user='user', host='host', port=1234, path='/absolute/path')"
)
assert Location("ssh://user@host:1234/some/path").to_key_filename() == keys_dir + "host___some_path"
assert Location("ssh://user@host:1234//absolute/path").to_key_filename() == keys_dir + "host___absolute_path"
assert (
repr(Location("ssh://user@host:1234/some/path"))
== "Location(proto='ssh', user='user', host='host', port=1234, path='/some/path')"
repr(Location("ssh://user@host:1234/relative/path"))
== "Location(proto='ssh', user='user', host='host', port=1234, path='relative/path')"
)
assert Location("ssh://user@host:1234/relative/path").to_key_filename() == keys_dir + "host__relative_path"
assert (
repr(Location("ssh://user@host/relative/path"))
== "Location(proto='ssh', user='user', host='host', port=None, path='relative/path')"
)
assert (
repr(Location("ssh://user@host/some/path"))
== "Location(proto='ssh', user='user', host='host', port=None, path='/some/path')"
repr(Location("ssh://user@[::]:1234/relative/path"))
== "Location(proto='ssh', user='user', host='::', port=1234, path='relative/path')"
)
assert Location("ssh://user@[::]:1234/relative/path").to_key_filename() == keys_dir + "____relative_path"
assert (
repr(Location("ssh://user@[::]/relative/path"))
== "Location(proto='ssh', user='user', host='::', port=None, path='relative/path')"
)
assert (
repr(Location("ssh://user@[::]:1234/some/path"))
== "Location(proto='ssh', user='user', host='::', port=1234, path='/some/path')"
repr(Location("ssh://user@[2001:db8::]:1234/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='relative/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')"
Location("ssh://user@[2001:db8::]:1234/relative/path").to_key_filename()
== keys_dir + "2001_db8____relative_path"
)
assert (
repr(Location("ssh://user@[2001:db8::]:1234/some/path"))
== "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path')"
repr(Location("ssh://user@[2001:db8::]/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='relative/path')"
)
assert (
repr(Location("ssh://user@[2001:db8::]:1234/some/path"))
== "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path')"
repr(Location("ssh://user@[2001:db8::c0:ffee]:1234/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='relative/path')"
)
assert (
Location("ssh://user@[2001:db8::]:1234/some/path").to_key_filename() == keys_dir + "2001_db8_____some_path"
repr(Location("ssh://user@[2001:db8::c0:ffee]/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='relative/path')"
)
assert (
repr(Location("ssh://user@[2001:db8::]/some/path"))
== "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path')"
repr(Location("ssh://user@[2001:db8::192.0.2.1]:1234/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='relative/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')"
repr(Location("ssh://user@[2001:db8::192.0.2.1]/relative/path"))
== "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='relative/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')"
Location("ssh://user@[2001:db8::192.0.2.1]/relative/path").to_key_filename()
== keys_dir + "2001_db8__192_0_2_1__relative_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')"
)
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]: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')"
)
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"))
repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]/relative/path"))
== "Location(proto='ssh', user='user', "
"host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='/some/path')"
"host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='relative/path')"
)
assert (
repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]:1234/some/path"))
repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]:1234/relative/path"))
== "Location(proto='ssh', user='user', "
"host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='/some/path')"
"host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='relative/path')"
)
def test_rclone(self, monkeypatch, keys_dir):
@ -250,41 +236,28 @@ class TestLocationWithoutEnv:
def test_abspath(self, monkeypatch, keys_dir):
monkeypatch.delenv("BORG_REPO", raising=False)
assert (
repr(Location("/some/absolute/path"))
== "Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path')"
repr(Location("/absolute/path"))
== "Location(proto='file', user=None, host=None, port=None, path='/absolute/path')"
)
assert Location("/absolute/path").to_key_filename() == keys_dir + "_absolute_path"
assert (
repr(Location("/some/absolute/path"))
== "Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path')"
repr(Location("ssh://user@host//absolute/path"))
== "Location(proto='ssh', user='user', host='host', port=None, path='/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')"
)
assert Location("ssh://user@host/some/path").to_key_filename() == keys_dir + "host___some_path"
assert Location("ssh://user@host//absolute/path").to_key_filename() == keys_dir + "host___absolute_path"
def test_relpath(self, monkeypatch, keys_dir):
monkeypatch.delenv("BORG_REPO", raising=False)
assert (
repr(Location("some/relative/path"))
== "Location(proto='file', user=None, host=None, port=None, path='some/relative/path')"
repr(Location("relative/path"))
== "Location(proto='file', user=None, host=None, port=None, path='relative/path')"
)
assert Location("relative/path").to_key_filename() == keys_dir + "relative_path"
assert (
repr(Location("some/relative/path"))
== "Location(proto='file', user=None, host=None, port=None, path='some/relative/path')"
repr(Location("ssh://user@host/relative/path"))
== "Location(proto='ssh', user='user', host='host', port=None, path='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')"
)
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')"
)
assert Location("ssh://user@host/~/some/path").to_key_filename() == keys_dir + "host_____some_path"
assert Location("ssh://user@host/relative/path").to_key_filename() == keys_dir + "host__relative_path"
def test_with_colons(self, monkeypatch, keys_dir):
monkeypatch.delenv("BORG_REPO", raising=False)
@ -310,7 +283,8 @@ class TestLocationWithoutEnv:
"host:some/path",
"host:~user/some/path",
"socket:///some/path",
"ssh://host/some/path",
"ssh://host/relative/path",
"ssh://host//absolute/path",
"ssh://user@host:1234/some/path",
]
for location in locations:

View file

@ -30,7 +30,7 @@ def repository(tmp_path):
def remote_repository(tmp_path):
if is_win32:
pytest.skip("Remote repository does not yet work on Windows.")
repository_location = Location("ssh://__testsuite__" + os.fspath(tmp_path / "repository"))
repository_location = Location("ssh://__testsuite__/" + os.fspath(tmp_path / "repository"))
yield LegacyRemoteRepository(repository_location, exclusive=True, create=True)

View file

@ -25,7 +25,7 @@ def repository(tmp_path):
def remote_repository(tmp_path):
if is_win32:
pytest.skip("Remote repository does not yet work on Windows.")
repository_location = Location("ssh://__testsuite__" + os.fspath(tmp_path / "repository"))
repository_location = Location("ssh://__testsuite__/" + os.fspath(tmp_path / "repository"))
yield RemoteRepository(repository_location, exclusive=True, create=True)