mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-16 08:14:32 +00:00
feat: docsgpt backup and docsgpt restore
`docsgpt backup` writes one archive holding a pg_dump of the database, a tar of each data volume and a manifest of what it came from; `docsgpt restore` puts it back over an install. The settings file is left out unless --with-settings asks for it, since it holds the install's secrets, and a backup taken with a newer DocsGPT is refused without --force. The volume tars go through the image the install already runs, so a backup pulls nothing extra, and compose calls can now redirect stdout and stdin so the dump never passes through this process.
This commit is contained in:
1 parent
4abd2c0c9f
commit
fe68fec69e
9 files changed
+489
-7
No files matched your search
@@ -1,8 +1,10 @@
|
||||
"""`docsgpt up` and the commands that manage the stack, against a fake Docker."""
|
||||
|
||||
import io
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -19,17 +21,38 @@ class FakeDocker:
|
||||
self.volumes = set(volumes)
|
||||
self.dirs = {Path(d) for d in project_dirs}
|
||||
self.calls = []
|
||||
self.volume_ops = []
|
||||
self.preflights = 0
|
||||
|
||||
def preflight(self, interactive=False):
|
||||
self.preflights += 1
|
||||
|
||||
def compose(self, directory, *args, capture=False, check=True):
|
||||
def compose(self, directory, *args, capture=False, check=True, stdout=None, stdin=None):
|
||||
self.calls.append((Path(directory), list(args)))
|
||||
if "down" in args and "-v" in args:
|
||||
self.volumes.clear()
|
||||
if stdout is not None:
|
||||
stdout.write("-- fake pg_dump\n")
|
||||
if stdin is not None:
|
||||
self.restored_sql = stdin.read()
|
||||
return subprocess.CompletedProcess(["docker", "compose", *args], 0, stdout="", stderr="")
|
||||
|
||||
def export_volume(self, volume, dest, image):
|
||||
"""Write a small tar, as the real one does with `docker run ... tar cf -`."""
|
||||
self.volume_ops.append(("export", volume, image))
|
||||
dest = Path(dest)
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
with tarfile.open(dest, "w") as tar:
|
||||
body = f"{volume} contents".encode()
|
||||
info = tarfile.TarInfo(f"{volume}.marker")
|
||||
info.size = len(body)
|
||||
tar.addfile(info, io.BytesIO(body))
|
||||
|
||||
def import_volume(self, volume, source, image):
|
||||
self.volume_ops.append(("import", volume, image))
|
||||
self.volumes.add(volume)
|
||||
assert Path(source).is_file(), source
|
||||
|
||||
def volume_exists(self, name):
|
||||
return name in self.volumes
|
||||
|
||||
|
||||
Reference in new issue
Block a user