mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-10-26 16:02:08 +00:00
Compare commits
2
Commits
1a50eea187
..
v7.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f824fabea1 | ||
|
|
07d819cb41 |
No files matched your search
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [7.1.0] - 2026-10-17
|
||||||
|
|
||||||
|
Fixed:
|
||||||
|
|
||||||
|
- Backup: rsync -a stored no xattrs, ACLs or hardlinks; now -aHAX
|
||||||
|
- Restore: restore_volume_files had the same gap; now -aHAX too
|
||||||
|
- An OpenCloud volume lost its storage metadata through a DR drill
|
||||||
|
|
||||||
|
Upgrade notes:
|
||||||
|
|
||||||
|
- First backup re-copies every attribute-carrying volume once in full
|
||||||
|
- A destination without xattr support now fails the backup run outright
|
||||||
|
- exFAT, xattr-less NFS and object-storage FUSE mounts must move first
|
||||||
|
|
||||||
## [7.0.2] - 2026-09-23
|
## [7.0.2] - 2026-09-23
|
||||||
|
|
||||||
* Backup: failed live pre-copy no longer aborts when a stopped copy follows
|
* Backup: failed live pre-copy no longer aborts when a stopped copy follows
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "backup-docker-to-local"
|
name = "backup-docker-to-local"
|
||||||
version = "7.0.2"
|
version = "7.1.0"
|
||||||
description = "Backup Docker volumes to local with rsync and optional DB dumps."
|
description = "Backup Docker volumes to local with rsync and optional DB dumps."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
@@ -19,7 +19,7 @@ dependencies = [
|
|||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
# Pinned: a ruff minor bump changes which rules fire, and `make test` gates on
|
# Pinned: a ruff minor bump changes which rules fire, and `make test` gates on
|
||||||
# a clean run, so an unpinned lint would fail the suite on an unrelated day.
|
# a clean run, so an unpinned lint would fail the suite on an unrelated day.
|
||||||
lint = ["ruff==0.17.0"]
|
lint = ["ruff==0.16.7"]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
baudolo = "baudolo.backup.__main__:main"
|
baudolo = "baudolo.backup.__main__:main"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ def backup_volume(
|
|||||||
pathlib.Path(dest).mkdir(parents=True, exist_ok=True)
|
pathlib.Path(dest).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
last = get_last_backup_dir(versions_dir, volume_name, dest)
|
last = get_last_backup_dir(versions_dir, volume_name, dest)
|
||||||
cmd = ["rsync", "-aP", "--no-D", "--delete", "--delete-excluded"]
|
cmd = ["rsync", "-aHAX", "-P", "--no-D", "--delete", "--delete-excluded"]
|
||||||
if authoritative:
|
if authoritative:
|
||||||
cmd.append("--checksum")
|
cmd.append("--checksum")
|
||||||
if last:
|
if last:
|
||||||
|
|||||||
@@ -59,6 +59,6 @@ def restore_volume_files(volume_name: str, backup_files_dir: str) -> int:
|
|||||||
# rsync reads "dir/" as its contents and "dir" as the directory itself.
|
# rsync reads "dir/" as its contents and "dir" as the directory itself.
|
||||||
src = f"{Path(backup_files_dir)}{os.sep}"
|
src = f"{Path(backup_files_dir)}{os.sep}"
|
||||||
dest = f"{Path(mountpoint)}{os.sep}"
|
dest = f"{Path(mountpoint)}{os.sep}"
|
||||||
run(["rsync", "-avv", "--delete", src, dest])
|
run(["rsync", "-aHAX", "-vv", "--delete", src, dest])
|
||||||
print("File restore complete.")
|
print("File restore complete.")
|
||||||
return 0
|
return 0
|
||||||
@@ -46,9 +46,12 @@ class TestBackupVolume(unittest.TestCase):
|
|||||||
def test_it_carries_no_kernel_objects_into_a_generation(self) -> None:
|
def test_it_carries_no_kernel_objects_into_a_generation(self) -> None:
|
||||||
self.assertIn("--no-D", self.copy())
|
self.assertIn("--no-D", self.copy())
|
||||||
|
|
||||||
|
def test_it_stores_the_metadata_a_restore_has_to_give_back(self) -> None:
|
||||||
|
self.assertIn("-aHAX", self.copy())
|
||||||
|
|
||||||
def test_it_keeps_no_twin_of_what_the_second_pass_replaces(self) -> None:
|
def test_it_keeps_no_twin_of_what_the_second_pass_replaces(self) -> None:
|
||||||
command = self.copy(authoritative=True)
|
command = self.copy(authoritative=True)
|
||||||
self.assertEqual(command[:2], ["rsync", "-aP"])
|
self.assertEqual(command[:3], ["rsync", "-aHAX", "-P"])
|
||||||
self.assertNotIn("--backup", command)
|
self.assertNotIn("--backup", command)
|
||||||
|
|
||||||
def test_it_creates_the_destination(self) -> None:
|
def test_it_creates_the_destination(self) -> None:
|
||||||
|
|||||||
Reference in new issue
Block a user