2
0
Эх сурвалжийг харах

add support for rclone:// repositories (via borgstore)

Thomas Waldmann 8 сар өмнө
parent
commit
bd6caf835d

+ 1 - 0
docs/quickstart.rst

@@ -411,6 +411,7 @@ Due to using the `borgstore` project, borg now also supports other kinds of
 
 
 - sftp: the borg client will directly talk to an sftp server.
 - sftp: the borg client will directly talk to an sftp server.
   This does not require borg being installed on the sftp server.
   This does not require borg being installed on the sftp server.
+- rclone: the borg client will talk via rclone to cloud storage.
 - Others may come in the future, adding backends to `borgstore` is rather simple.
 - Others may come in the future, adding backends to `borgstore` is rather simple.
 
 
 Restoring a backup
 Restoring a backup

+ 1 - 1
docs/usage/general/file-systems.rst.inc

@@ -24,7 +24,7 @@ Pros:
   and re-write segment files to free space.
   and re-write segment files to free space.
 - In future, easier to adapt to other kinds of storage:
 - In future, easier to adapt to other kinds of storage:
   borgstore's backends are quite simple to implement.
   borgstore's backends are quite simple to implement.
-  A ``sftp:`` backend already exists, cloud storage might be easy to add.
+  ``sftp:`` and ``rclone:`` backends already exists, others might be easy to add.
 - Parallel repository access with less locking is easier to implement.
 - Parallel repository access with less locking is easier to implement.
 
 
 Cons:
 Cons:

+ 7 - 2
docs/usage/general/repository-urls.rst.inc

@@ -14,7 +14,7 @@ Note: you may also prepend a ``file://`` to a filesystem path to get URL style.
 
 
 **Remote repositories** accessed via ssh user@host:
 **Remote repositories** accessed via ssh user@host:
 
 
-``ssh://user@host:port/path/to/repo`` - absolute path`
+``ssh://user@host:port/path/to/repo`` - absolute path
 
 
 ``ssh://user@host:port/./path/to/repo`` - path relative to current directory
 ``ssh://user@host:port/./path/to/repo`` - path relative to current directory
 
 
@@ -22,10 +22,15 @@ Note: you may also prepend a ``file://`` to a filesystem path to get URL style.
 
 
 **Remote repositories** accessed via sftp:
 **Remote repositories** accessed via sftp:
 
 
-``sftp://user@host:port/path/to/repo`` - absolute path`
+``sftp://user@host:port/path/to/repo`` - absolute path
 
 
 For ssh and sftp URLs, the ``user@`` and ``:port`` parts are optional.
 For ssh and sftp URLs, the ``user@`` and ``:port`` parts are optional.
 
 
+**Remote repositories** accessed via rclone:
+
+``rclone://remote:path``
+
+
 If you frequently need the same repo URL, it is a good idea to set the
 If you frequently need the same repo URL, it is a good idea to set the
 ``BORG_REPO`` environment variable to set a default for the repo URL:
 ``BORG_REPO`` environment variable to set a default for the repo URL:
 
 

+ 1 - 1
pyproject.toml

@@ -30,7 +30,7 @@ classifiers = [
 ]
 ]
 license = {text="BSD"}
 license = {text="BSD"}
 dependencies = [
 dependencies = [
-  "borgstore ~= 0.0.1",
+  "borgstore ~= 0.0.4",
   "msgpack >=1.0.3, <=1.1.0",
   "msgpack >=1.0.3, <=1.1.0",
   "packaging",
   "packaging",
   "platformdirs >=3.0.0, <5.0.0; sys_platform == 'darwin'",  # for macOS: breaking changes in 3.0.0,
   "platformdirs >=3.0.0, <5.0.0; sys_platform == 'darwin'",  # for macOS: breaking changes in 3.0.0,

+ 1 - 1
src/borg/archiver/_common.py

@@ -46,7 +46,7 @@ def get_repository(
             args=args,
             args=args,
         )
         )
 
 
-    elif location.proto in ("sftp", "file") and not v1_or_v2:  # stuff directly supported by borgstore
+    elif location.proto in ("sftp", "file", "rclone") and not v1_or_v2:  # stuff directly supported by borgstore
         repository = Repository(
         repository = Repository(
             location,
             location,
             create=create,
             create=create,

+ 14 - 1
src/borg/helpers/parseformat.py

@@ -467,6 +467,14 @@ class Location:
         re.VERBOSE,
         re.VERBOSE,
     )  # path
     )  # path
 
 
+    rclone_re = re.compile(
+        r"""
+        (?P<proto>rclone)://                                    # rclone://
+        (?P<path>(.*))
+        """,
+        re.VERBOSE,
+    )  # path
+
     socket_re = re.compile(
     socket_re = re.compile(
         r"""
         r"""
         (?P<proto>socket)://                                    # socket://
         (?P<proto>socket)://                                    # socket://
@@ -546,6 +554,11 @@ class Location:
             self.port = m.group("port") and int(m.group("port")) or None
             self.port = m.group("port") and int(m.group("port")) or None
             self.path = normpath_special(m.group("path"))
             self.path = normpath_special(m.group("path"))
             return True
             return True
+        m = self.rclone_re.match(text)
+        if m:
+            self.proto = m.group("proto")
+            self.path = m.group("path")
+            return True
         m = self.file_re.match(text)
         m = self.file_re.match(text)
         if m:
         if m:
             self.proto = m.group("proto")
             self.proto = m.group("proto")
@@ -575,7 +588,7 @@ class Location:
 
 
     def to_key_filename(self):
     def to_key_filename(self):
         name = re.sub(r"[^\w]", "_", self.path).strip("_")
         name = re.sub(r"[^\w]", "_", self.path).strip("_")
-        if self.proto not in ("file", "socket"):
+        if self.proto not in ("file", "socket", "rclone"):
             name = re.sub(r"[^\w]", "_", self.host) + "__" + name
             name = re.sub(r"[^\w]", "_", self.host) + "__" + name
         if len(name) > 100:
         if len(name) > 100:
             # Limit file names to some reasonable length. Most file systems
             # Limit file names to some reasonable length. Most file systems

+ 8 - 0
src/borg/testsuite/helpers.py

@@ -187,6 +187,14 @@ class TestLocationWithoutEnv:
             "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='/some/path')"
         )
         )
 
 
+    def test_rclone(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("rclone://remote:path"))
+            == "Location(proto='rclone', user=None, host=None, port=None, path='remote:path')"
+        )
+        assert Location("rclone://remote:path").to_key_filename() == keys_dir + "remote_path"
+
     def test_sftp(self, monkeypatch, keys_dir):
     def test_sftp(self, monkeypatch, keys_dir):
         monkeypatch.delenv("BORG_REPO", raising=False)
         monkeypatch.delenv("BORG_REPO", raising=False)
         assert (
         assert (