diff --git a/src/baudolo/backup/cli.py b/src/baudolo/backup/cli.py index 992ad65..db4d3b4 100644 --- a/src/baudolo/backup/cli.py +++ b/src/baudolo/backup/cli.py @@ -100,7 +100,9 @@ def parse_args() -> argparse.Namespace: if bool(args.snapshot) != bool(args.snapshot_subject): p.error("--snapshot and --snapshot-subject must be given together") if args.snapshot and args.shutdown: - p.error("--shutdown is meaningless with --snapshot: containers are never stopped") + p.error( + "--shutdown is meaningless with --snapshot: containers are never stopped" + ) if args.snapshot and args.hard_restart_projects: p.error( "--hard-restart-projects is meaningless with --snapshot: the flag exists " diff --git a/tests/e2e/helpers/fixtures.py b/tests/e2e/helpers/fixtures.py index 82a5c5c..d5e7cef 100644 --- a/tests/e2e/helpers/fixtures.py +++ b/tests/e2e/helpers/fixtures.py @@ -1,4 +1,5 @@ """Fixtures and paths the e2e suite builds its scenarios from.""" + from __future__ import annotations import shutil diff --git a/tests/e2e/helpers/process.py b/tests/e2e/helpers/process.py index 3a8f221..2e8ccf5 100644 --- a/tests/e2e/helpers/process.py +++ b/tests/e2e/helpers/process.py @@ -1,4 +1,5 @@ """Process, docker and readiness helpers for the e2e suite.""" + from __future__ import annotations import subprocess diff --git a/tests/e2e/snapshot_db_driver.py b/tests/e2e/snapshot_db_driver.py index 0cf5431..1871b36 100644 --- a/tests/e2e/snapshot_db_driver.py +++ b/tests/e2e/snapshot_db_driver.py @@ -25,7 +25,9 @@ GENERATION = f"{VERSIONS}/20260731" def shell(command: str) -> list[str]: proc = subprocess.run(command, shell=True, capture_output=True, text=True) if proc.returncode != 0: - raise SnapshotError(f"{command} exited {proc.returncode}: {proc.stderr.strip()}") + raise SnapshotError( + f"{command} exited {proc.returncode}: {proc.stderr.strip()}" + ) return proc.stdout.splitlines() diff --git a/tests/e2e/snapshot_driver.py b/tests/e2e/snapshot_driver.py index 696259c..370dd4d 100644 --- a/tests/e2e/snapshot_driver.py +++ b/tests/e2e/snapshot_driver.py @@ -22,7 +22,9 @@ EXPECT = sys.argv[3] def shell(command: str) -> list[str]: proc = subprocess.run(command, shell=True, capture_output=True, text=True) if proc.returncode != 0: - raise SnapshotError(f"{command} exited {proc.returncode}: {proc.stderr.strip()}") + raise SnapshotError( + f"{command} exited {proc.returncode}: {proc.stderr.strip()}" + ) return proc.stdout.splitlines() @@ -55,5 +57,8 @@ with volume_snapshot(KIND, SUBJECT, "e2e", run=shell) as resolve: root = Path(resolve(SUBJECT)) -check("the snapshot is removed afterwards", not root.exists() or not (root / "volumes").exists()) +check( + "the snapshot is removed afterwards", + not root.exists() or not (root / "volumes").exists(), +) print("ALL OK", flush=True) diff --git a/tests/e2e/test_e2e_snapshot.py b/tests/e2e/test_e2e_snapshot.py index fa38ac4..d8a4342 100644 --- a/tests/e2e/test_e2e_snapshot.py +++ b/tests/e2e/test_e2e_snapshot.py @@ -29,7 +29,7 @@ LOOP_FS = { "ext4": "mkdir -p /subject/docker", } # A container carries no /lib/modules, so modprobe fails even on a loaded module. -ZFS_READY = '{ [ -c /dev/zfs ] || modprobe zfs 2>/dev/null; }; [ -c /dev/zfs ]' +ZFS_READY = "{ [ -c /dev/zfs ] || modprobe zfs 2>/dev/null; }; [ -c /dev/zfs ]" def mount_script(fstype: str) -> str: @@ -51,7 +51,13 @@ def zfs_usable() -> bool: """Whether this host's kernel can serve zfs to a privileged container.""" proc = run( [ - "docker", "run", "--rm", "--privileged", IMAGE, "sh", "-lc", + "docker", + "run", + "--rm", + "--privileged", + IMAGE, + "sh", + "-lc", f"apk add -q zfs >/dev/null 2>&1 && {ZFS_READY}", ], capture=True, @@ -87,11 +93,20 @@ def drive(fstype: str, kind: str, expect: str) -> str: try: proc = run( [ - "docker", "run", "--rm", "--privileged", - "--name", staged.name, - "-v", f"{staged / 'src'}:/src:ro", - "-v", f"{staged / 'driver.py'}:/driver.py:ro", - IMAGE, "sh", "-lc", script, + "docker", + "run", + "--rm", + "--privileged", + "--name", + staged.name, + "-v", + f"{staged / 'src'}:/src:ro", + "-v", + f"{staged / 'driver.py'}:/driver.py:ro", + IMAGE, + "sh", + "-lc", + script, ], capture=True, check=False, @@ -99,7 +114,9 @@ def drive(fstype: str, kind: str, expect: str) -> str: finally: shutil.rmtree(staged, ignore_errors=True) if proc.returncode != 0: - raise AssertionError(f"{fstype}/{kind} driver failed:\n{proc.stdout}\n{proc.stderr}") + raise AssertionError( + f"{fstype}/{kind} driver failed:\n{proc.stdout}\n{proc.stderr}" + ) return proc.stdout @@ -125,7 +142,9 @@ class TestE2ESnapshot(unittest.TestCase): "E2E_REQUIRE_FILESYSTEMS demands zfs, but this kernel provides no " "zfs module; load it before running the suite" ) - self.skipTest("this kernel provides no zfs module, so no pool can be created") + self.skipTest( + "this kernel provides no zfs module, so no pool can be created" + ) self.assert_freezes("zfs") def test_ext4_has_no_snapshot_and_says_so(self) -> None: diff --git a/tests/e2e/test_e2e_snapshot_db.py b/tests/e2e/test_e2e_snapshot_db.py index d1cfa12..6a7b1c8 100644 --- a/tests/e2e/test_e2e_snapshot_db.py +++ b/tests/e2e/test_e2e_snapshot_db.py @@ -68,11 +68,20 @@ class TestE2ESnapshotDatabase(unittest.TestCase): try: proc = run( [ - "docker", "run", "--rm", "--privileged", - "--name", staged.name, - "-v", f"{staged / 'src'}:/src:ro", - "-v", f"{staged / 'driver.py'}:/driver.py:ro", - IMAGE, "sh", "-lc", SCRIPT, + "docker", + "run", + "--rm", + "--privileged", + "--name", + staged.name, + "-v", + f"{staged / 'src'}:/src:ro", + "-v", + f"{staged / 'driver.py'}:/driver.py:ro", + IMAGE, + "sh", + "-lc", + SCRIPT, ], capture=True, check=False, diff --git a/tests/unit/backup/test_app_snapshot.py b/tests/unit/backup/test_app_snapshot.py index 9878b87..5e21c7f 100644 --- a/tests/unit/backup/test_app_snapshot.py +++ b/tests/unit/backup/test_app_snapshot.py @@ -12,6 +12,7 @@ from baudolo.backup.snapshot import volume_snapshot def stubbed_snapshot(kind: str, subject: str, tag: str): return volume_snapshot(kind, subject, tag, run=lambda command: []) + ARGV = [ "baudolo", "--compose-dir", diff --git a/tests/unit/backup/test_cli.py b/tests/unit/backup/test_cli.py index e13b949..00a7539 100644 --- a/tests/unit/backup/test_cli.py +++ b/tests/unit/backup/test_cli.py @@ -39,7 +39,9 @@ class TestSnapshotFlags(unittest.TestCase): parse("--snapshot", "ext4", "--snapshot-subject", "/var/lib/docker") def test_zfs_is_accepted(self) -> None: - self.assertEqual(parse("--snapshot", "zfs", "--snapshot-subject", "/d").snapshot, "zfs") + self.assertEqual( + parse("--snapshot", "zfs", "--snapshot-subject", "/d").snapshot, "zfs" + ) def test_shutdown_is_rejected_because_nothing_is_stopped(self) -> None: with self.assertRaises(SystemExit): diff --git a/tests/unit/backup/test_snapshot.py b/tests/unit/backup/test_snapshot.py index 04901fd..7f68d44 100644 --- a/tests/unit/backup/test_snapshot.py +++ b/tests/unit/backup/test_snapshot.py @@ -35,11 +35,15 @@ class TestBtrfs(unittest.TestCase): run = Runner() with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run): pass - self.assertEqual(run.calls[-1], "btrfs subvolume delete /var/lib/docker/.baudolo-20260731") + self.assertEqual( + run.calls[-1], "btrfs subvolume delete /var/lib/docker/.baudolo-20260731" + ) def test_it_maps_a_volume_path_into_the_snapshot(self) -> None: run = Runner() - with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run) as resolve: + with volume_snapshot( + "btrfs", "/var/lib/docker", "20260731", run=run + ) as resolve: self.assertEqual( resolve("/var/lib/docker/volumes/postgres_data/_data"), "/var/lib/docker/.baudolo-20260731/volumes/postgres_data/_data", @@ -47,7 +51,9 @@ class TestBtrfs(unittest.TestCase): def test_it_keeps_the_trailing_slash_rsync_reads_as_contents(self) -> None: run = Runner() - with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run) as resolve: + with volume_snapshot( + "btrfs", "/var/lib/docker", "20260731", run=run + ) as resolve: self.assertEqual( resolve("/var/lib/docker/volumes/postgres_data/_data/"), "/var/lib/docker/.baudolo-20260731/volumes/postgres_data/_data/", @@ -102,14 +108,20 @@ class TestRejections(unittest.TestCase): def test_a_path_outside_the_subject_is_rejected(self) -> None: run = Runner() - with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run) as resolve: + with volume_snapshot( + "btrfs", "/var/lib/docker", "20260731", run=run + ) as resolve: with self.assertRaises(SnapshotError): resolve("/etc/passwd") def test_the_subject_itself_resolves_to_the_snapshot_root(self) -> None: run = Runner() - with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run) as resolve: - self.assertEqual(resolve("/var/lib/docker"), "/var/lib/docker/.baudolo-20260731") + with volume_snapshot( + "btrfs", "/var/lib/docker", "20260731", run=run + ) as resolve: + self.assertEqual( + resolve("/var/lib/docker"), "/var/lib/docker/.baudolo-20260731" + ) class Busy(Runner):