issue-6-restore-story #17

Merged
parkel merged 2 commits from issue-6-restore-story into master 2026-05-21 02:06:23 +02:00
4 changed files with 34 additions and 0 deletions
Showing only changes of commit 20a9f93378 - Show all commits

View File

@@ -181,6 +181,14 @@ restore, use another dry run before writing to the remote root:
rsync -aHAX --numeric-ids --info=progress2 --dry-run /backups/example.org/scheduled/<snapshot>/data/ root@example.org:/ rsync -aHAX --numeric-ids --info=progress2 --dry-run /backups/example.org/scheduled/<snapshot>/data/ root@example.org:/
``` ```
For most incidents, prefer a targeted restore instead of copying the whole snapshot. Keep paths relative to the
snapshot's `data/` directory:
```
rsync -aHAX --numeric-ids --info=progress2 --dry-run /backups/example.org/scheduled/<snapshot>/data/etc/nginx/ /restore/example.org/etc/nginx/
rsync -aHAX --numeric-ids --info=progress2 --dry-run /backups/example.org/scheduled/<snapshot>/data/home/example/site/public_html/index.php /restore/example.org/home/example/site/public_html/index.php
```
Snapshots may use hardlinks for files that are unchanged between backups. That saves disk space and is safe for normal Snapshots may use hardlinks for files that are unchanged between backups. That saves disk space and is safe for normal
restore copies, but do not edit files inside snapshot directories. Treat snapshots as read-only and copy data out with restore copies, but do not edit files inside snapshot directories. Treat snapshots as read-only and copy data out with
rsync. rsync.

View File

@@ -82,6 +82,16 @@
<div><strong>Restore to staging:</strong></div> <div><strong>Restore to staging:</strong></div>
<pre>{{ restore.local_command }}</pre> <pre>{{ restore.local_command }}</pre>
</div> </div>
<div class="stack spaced">
<div><strong>Dry-run a directory restore:</strong></div>
<pre>{{ restore.partial_dry_run_command }}</pre>
<div class="muted">Replace <code>{{ restore.example_relative_path }}</code> with the path you want to restore.</div>
</div>
<div class="stack spaced">
<div><strong>Dry-run a single file restore:</strong></div>
<pre>{{ restore.file_dry_run_command }}</pre>
<div class="muted">Replace <code>{{ restore.example_file_relative_path }}</code> with the file you want to restore.</div>
</div>
<div class="stack spaced"> <div class="stack spaced">
<div><strong>Dry-run restore back to the source host:</strong></div> <div><strong>Dry-run restore back to the source host:</strong></div>
<pre>{{ restore.remote_dry_run_command }}</pre> <pre>{{ restore.remote_dry_run_command }}</pre>

View File

@@ -1427,6 +1427,12 @@ class ViewTests(TestCase):
self.assertContains(response, "rsync -aHAX --numeric-ids --info=progress2 --dry-run") self.assertContains(response, "rsync -aHAX --numeric-ids --info=progress2 --dry-run")
self.assertContains(response, f"{base.path}/data/") self.assertContains(response, f"{base.path}/data/")
self.assertContains(response, "root@web-01.example.test:/") self.assertContains(response, "root@web-01.example.test:/")
self.assertContains(response, "Dry-run a directory restore")
self.assertContains(response, f"{base.path}/data/etc/nginx/")
self.assertContains(response, f"/restore/{host.host}/etc/nginx/")
self.assertContains(response, "Dry-run a single file restore")
self.assertContains(response, f"{base.path}/data/home/example/site/public_html/index.php")
self.assertContains(response, f"/restore/{host.host}/home/example/site/public_html/index.php")
self.assertContains(response, "Treat snapshot directories as read-only") self.assertContains(response, "Treat snapshot directories as read-only")
self.assertContains(response, child.dirname) self.assertContains(response, child.dirname)
self.assertContains(response, f"Run {run.id}") self.assertContains(response, f"Run {run.id}")

View File

@@ -796,17 +796,27 @@ def _pretty_json(value: object) -> str:
def _snapshot_restore_guidance(snapshot: SnapshotRecord) -> dict[str, str]: def _snapshot_restore_guidance(snapshot: SnapshotRecord) -> dict[str, str]:
source_path = Path(snapshot.path) / "data" source_path = Path(snapshot.path) / "data"
destination_path = Path("/restore") / snapshot.host.host destination_path = Path("/restore") / snapshot.host.host
example_relative_path = Path("etc") / "nginx"
example_file_relative_path = Path("home") / "example" / "site" / "public_html" / "index.php"
quoted_source = _quote_path_with_trailing_slash(source_path) quoted_source = _quote_path_with_trailing_slash(source_path)
quoted_destination = _quote_path_with_trailing_slash(destination_path) quoted_destination = _quote_path_with_trailing_slash(destination_path)
quoted_partial_source = _quote_path_with_trailing_slash(source_path / example_relative_path)
quoted_partial_destination = _quote_path_with_trailing_slash(destination_path / example_relative_path)
quoted_file_source = shlex.quote(str(source_path / example_file_relative_path))
quoted_file_destination = shlex.quote(str(destination_path / example_file_relative_path))
quoted_remote_destination = shlex.quote(f"root@{snapshot.host.address or snapshot.host.host}:/") quoted_remote_destination = shlex.quote(f"root@{snapshot.host.address or snapshot.host.host}:/")
common_args = "rsync -aHAX --numeric-ids --info=progress2" common_args = "rsync -aHAX --numeric-ids --info=progress2"
return { return {
"source_path": str(source_path), "source_path": str(source_path),
"destination_path": str(destination_path), "destination_path": str(destination_path),
"example_relative_path": str(example_relative_path),
"example_file_relative_path": str(example_file_relative_path),
"inspect_command": f"ls -la {quoted_source}", "inspect_command": f"ls -la {quoted_source}",
"dry_run_command": f"{common_args} --dry-run {quoted_source} {quoted_destination}", "dry_run_command": f"{common_args} --dry-run {quoted_source} {quoted_destination}",
"local_command": f"{common_args} {quoted_source} {quoted_destination}", "local_command": f"{common_args} {quoted_source} {quoted_destination}",
"partial_dry_run_command": f"{common_args} --dry-run {quoted_partial_source} {quoted_partial_destination}",
"file_dry_run_command": f"{common_args} --dry-run {quoted_file_source} {quoted_file_destination}",
"remote_dry_run_command": f"{common_args} --dry-run {quoted_source} {quoted_remote_destination}", "remote_dry_run_command": f"{common_args} --dry-run {quoted_source} {quoted_remote_destination}",
} }