mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-08-20 21:22:54 +00:00
feat(backup)!: mandatory repo-name and databases-csv, --only-files, --only-sql
Two defaults could not be right. --repo-name fell back to the literal 'backup-docker-to-local' while its help promised the git repo folder name, which nothing ever derived. --databases-csv pointed inside the installed package directory, where credentials must not live; when it applied, load_databases_df read a missing file as empty and the run finished without a single dump and without an error. Both are required now, --repo-name in the restore CLI too. The file itself may still be absent - babadcb's tolerance is untouched, only the path must be named. --everything is withdrawn. Its one effect was to ignore --images-no-stop-required, which is what leaving that list empty already does, and its branch was the default path minus the requires_stop check. No caller, no test, and help and README described it differently. --dump-only-sql becomes --only-sql, and --only-files joins it as the opposite half: no dumps at all, every volume as files. They form a mutually exclusive group. A host that only copies files has no business holding database passwords, so --databases-csv is not required there and is never read. The smallest valid argv turned out to be written four times across the test tree; it now lives once. Withdrawn flags are listed in one place and proven to exit 2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ def main() -> int:
|
||||
versions_dir = os.path.join(args.backups_dir, machine_id, args.repo_name)
|
||||
version_dir = create_version_directory(versions_dir, backup_time)
|
||||
|
||||
databases_df = load_databases_df(args.databases_csv)
|
||||
databases_df = None if args.only_files else load_databases_df(args.databases_csv)
|
||||
|
||||
print("💾 Start volume backups...", flush=True)
|
||||
|
||||
@@ -69,17 +69,19 @@ def main() -> int:
|
||||
|
||||
vol_dir = create_volume_directory(version_dir, volume_name)
|
||||
|
||||
found_db, dumped_any = backup_dumps_for_volume(
|
||||
containers=containers,
|
||||
vol_dir=vol_dir,
|
||||
databases_df=databases_df,
|
||||
database_containers=args.database_containers,
|
||||
)
|
||||
found_db = dumped_any = False
|
||||
if not args.only_files:
|
||||
found_db, dumped_any = backup_dumps_for_volume(
|
||||
containers=containers,
|
||||
vol_dir=vol_dir,
|
||||
databases_df=databases_df,
|
||||
database_containers=args.database_containers,
|
||||
)
|
||||
|
||||
if args.dump_only_sql and found_db:
|
||||
if args.only_sql and found_db:
|
||||
if not dumped_any:
|
||||
print(
|
||||
f"WARNING: dump-only-sql requested but no DB dump was produced for DB volume '{volume_name}'. "
|
||||
f"WARNING: only-sql requested but no DB dump was produced for DB volume '{volume_name}'. "
|
||||
"Falling back to file backup.",
|
||||
flush=True,
|
||||
)
|
||||
@@ -119,15 +121,6 @@ def main() -> int:
|
||||
copy(authoritative=False)
|
||||
continue
|
||||
|
||||
if args.everything:
|
||||
stoppable = filter_stoppable(containers)
|
||||
copy(authoritative=False)
|
||||
change_containers_status(stoppable, "stop")
|
||||
copy(authoritative=True)
|
||||
if not args.shutdown:
|
||||
change_containers_status(stoppable, "start")
|
||||
continue
|
||||
|
||||
copy(authoritative=False)
|
||||
if requires_stop(containers, args.images_no_stop_required):
|
||||
stoppable = filter_stoppable(containers)
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
dirname = os.path.dirname(__file__)
|
||||
default_databases_csv = os.path.join(dirname, "databases.csv")
|
||||
|
||||
p = argparse.ArgumentParser(description="Backup Docker volumes.")
|
||||
|
||||
p.add_argument(
|
||||
@@ -25,13 +21,12 @@ def parse_args() -> argparse.Namespace:
|
||||
|
||||
p.add_argument(
|
||||
"--repo-name",
|
||||
default="backup-docker-to-local",
|
||||
help="Backup repo folder name under <backups-dir>/<machine-id>/ (default: git repo folder name)",
|
||||
required=True,
|
||||
help="Backup repo folder name under <backups-dir>/<machine-id>/",
|
||||
)
|
||||
p.add_argument(
|
||||
"--databases-csv",
|
||||
default=default_databases_csv,
|
||||
help=f"Path to databases.csv (default: {default_databases_csv})",
|
||||
help="Path to databases.csv; required unless --only-files is given",
|
||||
)
|
||||
p.add_argument(
|
||||
"--backups-dir",
|
||||
@@ -75,19 +70,15 @@ def parse_args() -> argparse.Namespace:
|
||||
help="Exact volume names that are never backed up, whatever containers use them. For derived trees a restore cannot reproduce, above all a nested docker data root",
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"--everything",
|
||||
action="store_true",
|
||||
help="Force file backup for all volumes and also execute database dumps (like old script)",
|
||||
)
|
||||
p.add_argument(
|
||||
"--shutdown",
|
||||
action="store_true",
|
||||
help="Do not restart containers after backup",
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"--dump-only-sql",
|
||||
scope = p.add_mutually_exclusive_group()
|
||||
scope.add_argument(
|
||||
"--only-sql",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Create database dumps only for DB volumes. "
|
||||
@@ -96,7 +87,19 @@ def parse_args() -> argparse.Namespace:
|
||||
"If a DB dump cannot be produced, baudolo falls back to a file backup."
|
||||
),
|
||||
)
|
||||
scope.add_argument(
|
||||
"--only-files",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Take no database dumps at all and back up every volume as files. "
|
||||
"For hosts that hold no database credentials. A database's files "
|
||||
"are only consistent if its containers are stopped for the second "
|
||||
"pass, so keep its image off --images-no-stop-required."
|
||||
),
|
||||
)
|
||||
args = p.parse_args()
|
||||
if not args.only_files and not args.databases_csv:
|
||||
p.error("--databases-csv is required unless --only-files is given")
|
||||
if bool(args.snapshot) != bool(args.snapshot_subject):
|
||||
p.error("--snapshot and --snapshot-subject must be given together")
|
||||
if args.snapshot and args.shutdown:
|
||||
|
||||
@@ -22,8 +22,8 @@ def _add_common_backup_args(p: argparse.ArgumentParser) -> None:
|
||||
)
|
||||
p.add_argument(
|
||||
"--repo-name",
|
||||
default="backup-docker-to-local",
|
||||
help="Backup repo folder name under <backups-dir>/<hash>/ (default: backup-docker-to-local)",
|
||||
required=True,
|
||||
help="Backup repo folder name under <backups-dir>/<hash>/",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user