mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-08-12 17:34:39 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 756e236d10 | |||
| 1dfeb17ab4 | |||
| cd21f1fa67 |
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"ask": [
|
"ask": [
|
||||||
|
"Bash(git commit*)",
|
||||||
"Edit(CHANGELOG.md)",
|
"Edit(CHANGELOG.md)",
|
||||||
"Write(CHANGELOG.md)",
|
"Write(CHANGELOG.md)",
|
||||||
"Edit(pyproject.toml)",
|
"Edit(pyproject.toml)",
|
||||||
|
|||||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,5 +1,27 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [3.4.1] - 2026-08-05
|
||||||
|
|
||||||
|
- Backup: each volume is copied twice into the same destination — once hot,
|
||||||
|
once cold after the container is stopped — and rsync ran with *-b*, so
|
||||||
|
*--delete* renamed rather than removed a file the source had dropped between
|
||||||
|
the passes. Stopping a container is what makes the source drop files: a
|
||||||
|
graceful shutdown flushes and the format rolls its commit point. The
|
||||||
|
superseded file survived as *name~* beside the real one and was restored into
|
||||||
|
live data.
|
||||||
|
- Backup: for an opaque payload that is stale bytes nobody reads; for a format
|
||||||
|
that enumerates its own directory it is corruption. Lucene resolves the
|
||||||
|
current commit by parsing every file starting with *segments* as a radix-36
|
||||||
|
generation, so a restored *segments_3~* leaves the shard store unreadable and
|
||||||
|
the primary at *NO_VALID_SHARD_COPY*. With *.security-7* unallocatable the
|
||||||
|
reserved *elastic* user has no password hash, every probe answers 401 and the
|
||||||
|
container never turns healthy.
|
||||||
|
- Backup: *--link-dest* already provides the incrementals and nothing reads the
|
||||||
|
twins — the restore path is an unfiltered *rsync -avv --delete* into the live
|
||||||
|
volume. Dropping *-b* leaves the predecessor generation byte-identical, keeps
|
||||||
|
the hardlinks intact and makes generations smaller, never larger.
|
||||||
|
- Tests: the absence of *--backup* is asserted on the rsync invocation.
|
||||||
|
|
||||||
## [3.4.0] - 2026-08-02
|
## [3.4.0] - 2026-08-02
|
||||||
|
|
||||||
- Backup: *-a* implies *-D*, so a generation was written with
|
- Backup: *-a* implies *-D*, so a generation was written with
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "backup-docker-to-local"
|
name = "backup-docker-to-local"
|
||||||
version = "3.4.0"
|
version = "3.4.1"
|
||||||
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"
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def backup_volume(
|
|||||||
verify = "--checksum " if authoritative else ""
|
verify = "--checksum " if authoritative else ""
|
||||||
|
|
||||||
cmd = (
|
cmd = (
|
||||||
f"rsync -abP --no-D --delete --delete-excluded "
|
f"rsync -aP --no-D --delete --delete-excluded "
|
||||||
f"{verify}{link_dest} {source} {dest}"
|
f"{verify}{link_dest} {source} {dest}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ 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_keeps_no_twin_of_what_the_second_pass_replaces(self) -> None:
|
||||||
|
command = self.copy(authoritative=True)
|
||||||
|
self.assertIn("rsync -aP ", command)
|
||||||
|
self.assertNotIn("--backup", command)
|
||||||
|
|
||||||
def test_it_creates_the_destination(self) -> None:
|
def test_it_creates_the_destination(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
dest = Path(tmp) / "gen" / "demo"
|
dest = Path(tmp) / "gen" / "demo"
|
||||||
|
|||||||
Reference in New Issue
Block a user