mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-07-17 06:05:13 +00:00
refactor(restore)!: restore volume files via direct host rsync
Drop the alpine-rsync helper image: resolve the volume mountpoint with docker volume inspect and rsync the backup straight into it. Removes the --rsync-image flag and the E2E_RSYNC_IMAGE pre-pull; the e2e test container now mounts /var/lib/docker rw so the direct restore can write. BREAKING CHANGE: the restore 'files' subcommand no longer accepts --rsync-image; rsync must be available on the host running baudolo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,6 @@ dind() { docker exec "${DIND}" docker "$@"; }
|
|||||||
dind_stdin() { docker exec -i "${DIND}" docker "$@"; }
|
dind_stdin() { docker exec -i "${DIND}" docker "$@"; }
|
||||||
|
|
||||||
IMG="${E2E_IMAGE:-baudolo:local}"
|
IMG="${E2E_IMAGE:-baudolo:local}"
|
||||||
RSYNC_IMG="${E2E_RSYNC_IMAGE:-ghcr.io/kevinveenbirkenbach/alpine-rsync}"
|
|
||||||
|
|
||||||
READY_TIMEOUT_SECONDS="${E2E_READY_TIMEOUT_SECONDS:-120}"
|
READY_TIMEOUT_SECONDS="${E2E_READY_TIMEOUT_SECONDS:-120}"
|
||||||
ARTIFACTS_DIR="${E2E_ARTIFACTS_DIR:-./artifacts}"
|
ARTIFACTS_DIR="${E2E_ARTIFACTS_DIR:-./artifacts}"
|
||||||
@@ -164,10 +163,6 @@ for i in $(seq 1 "${READY_TIMEOUT_SECONDS}"); do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
log "Pre-pulling helper images in DinD..."
|
|
||||||
log " - Pulling: ${RSYNC_IMG}"
|
|
||||||
dind pull "${RSYNC_IMG}"
|
|
||||||
|
|
||||||
log "Ensuring alpine exists in DinD (for debug helpers)"
|
log "Ensuring alpine exists in DinD (for debug helpers)"
|
||||||
dind pull alpine:3.20 >/dev/null
|
dind pull alpine:3.20 >/dev/null
|
||||||
|
|
||||||
@@ -181,8 +176,7 @@ if [ "${DEBUG_SHELL}" = "1" ]; then
|
|||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
--network "${NET}" \
|
--network "${NET}" \
|
||||||
-e DOCKER_HOST="${DIND_HOST_IN_NET}" \
|
-e DOCKER_HOST="${DIND_HOST_IN_NET}" \
|
||||||
-e E2E_RSYNC_IMAGE="${RSYNC_IMG}" \
|
-v "${DIND_VOL}:/var/lib/docker" \
|
||||||
-v "${DIND_VOL}:/var/lib/docker:ro" \
|
|
||||||
-v "${E2E_TMP_VOL}:/tmp" \
|
-v "${E2E_TMP_VOL}:/tmp" \
|
||||||
"${IMG}" \
|
"${IMG}" \
|
||||||
bash -lc '
|
bash -lc '
|
||||||
@@ -200,8 +194,7 @@ else
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
--network "${NET}" \
|
--network "${NET}" \
|
||||||
-e DOCKER_HOST="${DIND_HOST_IN_NET}" \
|
-e DOCKER_HOST="${DIND_HOST_IN_NET}" \
|
||||||
-e E2E_RSYNC_IMAGE="${RSYNC_IMG}" \
|
-v "${DIND_VOL}:/var/lib/docker" \
|
||||||
-v "${DIND_VOL}:/var/lib/docker:ro" \
|
|
||||||
-v "${E2E_TMP_VOL}:/tmp" \
|
-v "${E2E_TMP_VOL}:/tmp" \
|
||||||
"${IMG}" \
|
"${IMG}" \
|
||||||
bash -lc '
|
bash -lc '
|
||||||
|
|||||||
@@ -38,10 +38,6 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
p_files = sub.add_parser("files", help="Restore files into a docker volume")
|
p_files = sub.add_parser("files", help="Restore files into a docker volume")
|
||||||
_add_common_backup_args(p_files)
|
_add_common_backup_args(p_files)
|
||||||
p_files.add_argument(
|
|
||||||
"--rsync-image",
|
|
||||||
default="ghcr.io/kevinveenbirkenbach/alpine-rsync",
|
|
||||||
)
|
|
||||||
p_files.add_argument(
|
p_files.add_argument(
|
||||||
"--source-volume",
|
"--source-volume",
|
||||||
default=None,
|
default=None,
|
||||||
@@ -95,7 +91,6 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
return restore_volume_files(
|
return restore_volume_files(
|
||||||
args.volume_name,
|
args.volume_name,
|
||||||
bp_files.files_dir(),
|
bp_files.files_dir(),
|
||||||
rsync_image=args.rsync_image,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.cmd == "postgres":
|
if args.cmd == "postgres":
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .run import run, docker_volume_exists
|
from .run import docker_volume_exists, run
|
||||||
|
|
||||||
|
|
||||||
def restore_volume_files(
|
def restore_volume_files(volume_name: str, backup_files_dir: str) -> int:
|
||||||
volume_name: str, backup_files_dir: str, *, rsync_image: str
|
|
||||||
) -> int:
|
|
||||||
if not os.path.isdir(backup_files_dir):
|
if not os.path.isdir(backup_files_dir):
|
||||||
print(f"ERROR: backup files dir not found: {backup_files_dir}", file=sys.stderr)
|
print(f"ERROR: backup files dir not found: {backup_files_dir}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
@@ -19,21 +17,21 @@ def restore_volume_files(
|
|||||||
else:
|
else:
|
||||||
print(f"Volume {volume_name} already exists.")
|
print(f"Volume {volume_name} already exists.")
|
||||||
|
|
||||||
# Keep behavior close to the old script: rsync -avv --delete
|
cp = run(
|
||||||
run(
|
["docker", "volume", "inspect", "--format", "{{ .Mountpoint }}", volume_name],
|
||||||
[
|
capture=True,
|
||||||
"docker",
|
|
||||||
"run",
|
|
||||||
"--rm",
|
|
||||||
"-v",
|
|
||||||
f"{volume_name}:/recover/",
|
|
||||||
"-v",
|
|
||||||
f"{backup_files_dir}:/backup/",
|
|
||||||
rsync_image,
|
|
||||||
"sh",
|
|
||||||
"-lc",
|
|
||||||
"rsync -avv --delete /backup/ /recover/",
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
raw = cp.stdout or b""
|
||||||
|
mountpoint = (raw.decode() if isinstance(raw, bytes) else raw).strip()
|
||||||
|
if not mountpoint:
|
||||||
|
print(
|
||||||
|
f"ERROR: could not resolve mountpoint for volume {volume_name}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
src = os.path.join(backup_files_dir, "")
|
||||||
|
dest = os.path.join(mountpoint, "")
|
||||||
|
run(["rsync", "-avv", "--delete", src, dest])
|
||||||
print("File restore complete.")
|
print("File restore complete.")
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user