mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-08-02 13:02:41 +00:00
Autolint
This commit is contained in:
@@ -100,7 +100,9 @@ def parse_args() -> argparse.Namespace:
|
|||||||
if bool(args.snapshot) != bool(args.snapshot_subject):
|
if bool(args.snapshot) != bool(args.snapshot_subject):
|
||||||
p.error("--snapshot and --snapshot-subject must be given together")
|
p.error("--snapshot and --snapshot-subject must be given together")
|
||||||
if args.snapshot and args.shutdown:
|
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:
|
if args.snapshot and args.hard_restart_projects:
|
||||||
p.error(
|
p.error(
|
||||||
"--hard-restart-projects is meaningless with --snapshot: the flag exists "
|
"--hard-restart-projects is meaningless with --snapshot: the flag exists "
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""Fixtures and paths the e2e suite builds its scenarios from."""
|
"""Fixtures and paths the e2e suite builds its scenarios from."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""Process, docker and readiness helpers for the e2e suite."""
|
"""Process, docker and readiness helpers for the e2e suite."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ GENERATION = f"{VERSIONS}/20260731"
|
|||||||
def shell(command: str) -> list[str]:
|
def shell(command: str) -> list[str]:
|
||||||
proc = subprocess.run(command, shell=True, capture_output=True, text=True)
|
proc = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
if proc.returncode != 0:
|
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()
|
return proc.stdout.splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ EXPECT = sys.argv[3]
|
|||||||
def shell(command: str) -> list[str]:
|
def shell(command: str) -> list[str]:
|
||||||
proc = subprocess.run(command, shell=True, capture_output=True, text=True)
|
proc = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
if proc.returncode != 0:
|
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()
|
return proc.stdout.splitlines()
|
||||||
|
|
||||||
|
|
||||||
@@ -55,5 +57,8 @@ with volume_snapshot(KIND, SUBJECT, "e2e", run=shell) as resolve:
|
|||||||
|
|
||||||
root = Path(resolve(SUBJECT))
|
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)
|
print("ALL OK", flush=True)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ LOOP_FS = {
|
|||||||
"ext4": "mkdir -p /subject/docker",
|
"ext4": "mkdir -p /subject/docker",
|
||||||
}
|
}
|
||||||
# A container carries no /lib/modules, so modprobe fails even on a loaded module.
|
# 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:
|
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."""
|
"""Whether this host's kernel can serve zfs to a privileged container."""
|
||||||
proc = run(
|
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}",
|
f"apk add -q zfs >/dev/null 2>&1 && {ZFS_READY}",
|
||||||
],
|
],
|
||||||
capture=True,
|
capture=True,
|
||||||
@@ -87,11 +93,20 @@ def drive(fstype: str, kind: str, expect: str) -> str:
|
|||||||
try:
|
try:
|
||||||
proc = run(
|
proc = run(
|
||||||
[
|
[
|
||||||
"docker", "run", "--rm", "--privileged",
|
"docker",
|
||||||
"--name", staged.name,
|
"run",
|
||||||
"-v", f"{staged / 'src'}:/src:ro",
|
"--rm",
|
||||||
"-v", f"{staged / 'driver.py'}:/driver.py:ro",
|
"--privileged",
|
||||||
IMAGE, "sh", "-lc", script,
|
"--name",
|
||||||
|
staged.name,
|
||||||
|
"-v",
|
||||||
|
f"{staged / 'src'}:/src:ro",
|
||||||
|
"-v",
|
||||||
|
f"{staged / 'driver.py'}:/driver.py:ro",
|
||||||
|
IMAGE,
|
||||||
|
"sh",
|
||||||
|
"-lc",
|
||||||
|
script,
|
||||||
],
|
],
|
||||||
capture=True,
|
capture=True,
|
||||||
check=False,
|
check=False,
|
||||||
@@ -99,7 +114,9 @@ def drive(fstype: str, kind: str, expect: str) -> str:
|
|||||||
finally:
|
finally:
|
||||||
shutil.rmtree(staged, ignore_errors=True)
|
shutil.rmtree(staged, ignore_errors=True)
|
||||||
if proc.returncode != 0:
|
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
|
return proc.stdout
|
||||||
|
|
||||||
|
|
||||||
@@ -125,7 +142,9 @@ class TestE2ESnapshot(unittest.TestCase):
|
|||||||
"E2E_REQUIRE_FILESYSTEMS demands zfs, but this kernel provides no "
|
"E2E_REQUIRE_FILESYSTEMS demands zfs, but this kernel provides no "
|
||||||
"zfs module; load it before running the suite"
|
"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")
|
self.assert_freezes("zfs")
|
||||||
|
|
||||||
def test_ext4_has_no_snapshot_and_says_so(self) -> None:
|
def test_ext4_has_no_snapshot_and_says_so(self) -> None:
|
||||||
|
|||||||
@@ -68,11 +68,20 @@ class TestE2ESnapshotDatabase(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
proc = run(
|
proc = run(
|
||||||
[
|
[
|
||||||
"docker", "run", "--rm", "--privileged",
|
"docker",
|
||||||
"--name", staged.name,
|
"run",
|
||||||
"-v", f"{staged / 'src'}:/src:ro",
|
"--rm",
|
||||||
"-v", f"{staged / 'driver.py'}:/driver.py:ro",
|
"--privileged",
|
||||||
IMAGE, "sh", "-lc", SCRIPT,
|
"--name",
|
||||||
|
staged.name,
|
||||||
|
"-v",
|
||||||
|
f"{staged / 'src'}:/src:ro",
|
||||||
|
"-v",
|
||||||
|
f"{staged / 'driver.py'}:/driver.py:ro",
|
||||||
|
IMAGE,
|
||||||
|
"sh",
|
||||||
|
"-lc",
|
||||||
|
SCRIPT,
|
||||||
],
|
],
|
||||||
capture=True,
|
capture=True,
|
||||||
check=False,
|
check=False,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from baudolo.backup.snapshot import volume_snapshot
|
|||||||
def stubbed_snapshot(kind: str, subject: str, tag: str):
|
def stubbed_snapshot(kind: str, subject: str, tag: str):
|
||||||
return volume_snapshot(kind, subject, tag, run=lambda command: [])
|
return volume_snapshot(kind, subject, tag, run=lambda command: [])
|
||||||
|
|
||||||
|
|
||||||
ARGV = [
|
ARGV = [
|
||||||
"baudolo",
|
"baudolo",
|
||||||
"--compose-dir",
|
"--compose-dir",
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ class TestSnapshotFlags(unittest.TestCase):
|
|||||||
parse("--snapshot", "ext4", "--snapshot-subject", "/var/lib/docker")
|
parse("--snapshot", "ext4", "--snapshot-subject", "/var/lib/docker")
|
||||||
|
|
||||||
def test_zfs_is_accepted(self) -> None:
|
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:
|
def test_shutdown_is_rejected_because_nothing_is_stopped(self) -> None:
|
||||||
with self.assertRaises(SystemExit):
|
with self.assertRaises(SystemExit):
|
||||||
|
|||||||
@@ -35,11 +35,15 @@ class TestBtrfs(unittest.TestCase):
|
|||||||
run = Runner()
|
run = Runner()
|
||||||
with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run):
|
with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run):
|
||||||
pass
|
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:
|
def test_it_maps_a_volume_path_into_the_snapshot(self) -> None:
|
||||||
run = Runner()
|
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(
|
self.assertEqual(
|
||||||
resolve("/var/lib/docker/volumes/postgres_data/_data"),
|
resolve("/var/lib/docker/volumes/postgres_data/_data"),
|
||||||
"/var/lib/docker/.baudolo-20260731/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:
|
def test_it_keeps_the_trailing_slash_rsync_reads_as_contents(self) -> None:
|
||||||
run = Runner()
|
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(
|
self.assertEqual(
|
||||||
resolve("/var/lib/docker/volumes/postgres_data/_data/"),
|
resolve("/var/lib/docker/volumes/postgres_data/_data/"),
|
||||||
"/var/lib/docker/.baudolo-20260731/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:
|
def test_a_path_outside_the_subject_is_rejected(self) -> None:
|
||||||
run = Runner()
|
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):
|
with self.assertRaises(SnapshotError):
|
||||||
resolve("/etc/passwd")
|
resolve("/etc/passwd")
|
||||||
|
|
||||||
def test_the_subject_itself_resolves_to_the_snapshot_root(self) -> None:
|
def test_the_subject_itself_resolves_to_the_snapshot_root(self) -> None:
|
||||||
run = Runner()
|
run = Runner()
|
||||||
with volume_snapshot("btrfs", "/var/lib/docker", "20260731", run=run) as resolve:
|
with volume_snapshot(
|
||||||
self.assertEqual(resolve("/var/lib/docker"), "/var/lib/docker/.baudolo-20260731")
|
"btrfs", "/var/lib/docker", "20260731", run=run
|
||||||
|
) as resolve:
|
||||||
|
self.assertEqual(
|
||||||
|
resolve("/var/lib/docker"), "/var/lib/docker/.baudolo-20260731"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Busy(Runner):
|
class Busy(Runner):
|
||||||
|
|||||||
Reference in New Issue
Block a user