(docs) Add targeted restore examples
Extend the restore guidance with directory and single-file dry-run examples so operators can restore a focused path without copying an entire snapshot. Render the examples on snapshot detail pages using the selected snapshot's data path and the host-specific staging destination.
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -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}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user