backup: Use tar --transform to arrange the tarball instead of symlinks.

This allows tar to print the real paths in error messages if something
goes wrong.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
This commit is contained in:
Anders Kaseorg
2019-04-12 16:48:34 -07:00
committed by Tim Abbott
parent abe645f1d3
commit e074165c1b
2 changed files with 41 additions and 17 deletions

View File

@@ -2,6 +2,7 @@
import argparse import argparse
import os import os
import re
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@@ -56,13 +57,16 @@ if __name__ == "__main__":
os.chown(path, uid, gid) os.chown(path, uid, gid)
os.setresuid(uid, uid, 0) os.setresuid(uid, uid, 0)
# We create symlinks so that we can do a single `tar -x` assert not any("|" in name or "|" in path for name, path in paths)
# command to unpack the uploads, settings, etc. to their transform_args = [
# appropriate places. r"--transform=s|^zulip-backup/{}(/.*)?$|{}\1|x".format(
re.escape(name), path.replace("\\", r"\\")
)
for name, path in paths
]
os.mkdir(os.path.join(tmp, "zulip-backup")) os.mkdir(os.path.join(tmp, "zulip-backup"))
for name, path in paths: run(["tar", "-C", tmp] + transform_args + ["-xPzf", args.tarball])
os.symlink(path, os.path.join(tmp, "zulip-backup", name))
run(["tar", "-C", tmp, "--keep-directory-symlink", "-xzf", args.tarball])
# Now, restore the the database backup using pg_restore. # Now, restore the the database backup using pg_restore.
db_name = settings.DATABASES["default"]["NAME"] db_name = settings.DATABASES["default"]["NAME"]

View File

@@ -1,4 +1,5 @@
import os import os
import re
import tempfile import tempfile
from argparse import ArgumentParser, RawTextHelpFormatter from argparse import ArgumentParser, RawTextHelpFormatter
from typing import Any from typing import Any
@@ -33,6 +34,7 @@ class Command(ZulipBaseCommand):
) as tmp: ) as tmp:
os.mkdir(os.path.join(tmp, "zulip-backup")) os.mkdir(os.path.join(tmp, "zulip-backup"))
members = [] members = []
paths = []
with open(os.path.join(tmp, "zulip-backup", "zulip-version"), "w") as f: with open(os.path.join(tmp, "zulip-backup", "zulip-version"), "w") as f:
print(ZULIP_VERSION, file=f) print(ZULIP_VERSION, file=f)
@@ -53,14 +55,15 @@ class Command(ZulipBaseCommand):
members.append("zulip-backup/postgres-version") members.append("zulip-backup/postgres-version")
if settings.DEVELOPMENT: if settings.DEVELOPMENT:
os.symlink( members.append(
os.path.join(settings.DEPLOY_ROOT, "zproject"), os.path.join(settings.DEPLOY_ROOT, "zproject", "dev-secrets.conf")
os.path.join(tmp, "zulip-backup", "zproject"), )
paths.append(
("zproject", os.path.join(settings.DEPLOY_ROOT, "zproject"))
) )
members.append("zulip-backup/zproject/dev-secrets.conf")
else: else:
os.symlink("/etc/zulip", os.path.join(tmp, "zulip-backup", "settings")) members.append("/etc/zulip")
members.append("zulip-backup/settings") paths.append(("settings", "/etc/zulip"))
db_name = settings.DATABASES["default"]["NAME"] db_name = settings.DATABASES["default"]["NAME"]
db_dir = os.path.join(tmp, "zulip-backup", "database") db_dir = os.path.join(tmp, "zulip-backup", "database")
@@ -73,11 +76,23 @@ class Command(ZulipBaseCommand):
if settings.LOCAL_UPLOADS_DIR is not None and os.path.exists( if settings.LOCAL_UPLOADS_DIR is not None and os.path.exists(
os.path.join(settings.DEPLOY_ROOT, settings.LOCAL_UPLOADS_DIR) os.path.join(settings.DEPLOY_ROOT, settings.LOCAL_UPLOADS_DIR)
): ):
os.symlink( members.append(
os.path.join(settings.DEPLOY_ROOT, settings.LOCAL_UPLOADS_DIR), os.path.join(settings.DEPLOY_ROOT, settings.LOCAL_UPLOADS_DIR)
os.path.join(tmp, "zulip-backup", "uploads"),
) )
members.append("zulip-backup/uploads") paths.append(
(
"uploads",
os.path.join(settings.DEPLOY_ROOT, settings.LOCAL_UPLOADS_DIR),
)
)
assert not any("|" in name or "|" in path for name, path in paths)
transform_args = [
r"--transform=s|^{}(/.*)?$|zulip-backup/{}\1|x".format(
re.escape(path), name.replace("\\", r"\\")
)
for name, path in paths
]
try: try:
if options["output"] is None: if options["output"] is None:
@@ -89,7 +104,12 @@ class Command(ZulipBaseCommand):
else: else:
tarball_path = options["output"] tarball_path = options["output"]
run(["tar", "-C", tmp, "-chzf", tarball_path, "--"] + members) run(
["tar", "-C", tmp, "-cPzf", tarball_path]
+ transform_args
+ ["--"]
+ members
)
print("Backup tarball written to %s" % (tarball_path,)) print("Backup tarball written to %s" % (tarball_path,))
except BaseException: except BaseException:
if options["output"] is None: if options["output"] is None: