Sanitized single-commit public mirror of recipe-maintainer. - Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders. - Removed plans/ and planned-updates/ (deployment-planning docs) so no client/ deployment domains appear in the public repo. - All other secret stores were already gitignored. - docs.coopcloud.tech retained as a submodule (public upstream).
11 KiB
description, argument-hint, allowed-tools
| description | argument-hint | allowed-tools | ||||||
|---|---|---|---|---|---|---|---|---|
| Test backing up and restoring a recipe's test instance | <recipe-name> |
|
Recipe Backup Test
Test the full backup and restore cycle for a Co-op Cloud recipe: create a backup, tear down the test instance (undeploy + remove volumes), redeploy from scratch, restore from the backup, and verify the app still works.
Important: All abra commands that read the recipe (deploy, backup, restore, restart, ps) MUST use --chaos so they use the current local recipe checkout, including any uncommitted changes. This ensures we're testing the backup/restore labels as they exist in the working copy.
TTY workaround: Several abra subcommands (backup create, backup snapshots, restore, volume remove) fail with "the input device is not a TTY" in non-interactive environments. Wrap these with script -qefc "..." /dev/null to provide a pseudo-TTY.
The recipe name is: $ARGUMENTS
Read and follow the instructions in .claude/commands/includes/logging.md.
Read and follow the instructions in .claude/commands/includes/guidelines.md.
Read ~/.abra/recipes/backup-bot-two/README.md for context on how backup-bot-two works (its CLI commands, backup/restore flow, and label conventions).
Steps
-
Free server resources by running
/test-context-reset $ARGUMENTSto undeploy unrelated apps from the test server while keeping this recipe and its dependencies running. -
Get the domain and server for this recipe:
python3 scripts/get_test_instance.py --recipe $ARGUMENTSThis outputs DOMAIN and SERVER for the active instance. Also read
settings.tomlto getDOMAIN_SUFFIX(thedomain_suffixfield for the active instance).- If the recipe has no
recipe-info/$ARGUMENTS/recipe.toml, tell the user to run/recipe-init $ARGUMENTSfirst and stop.
- If the recipe has no
-
Read recipe-specific backup test notes from
recipe-info/$ARGUMENTS/tests/backup-test.md, if it exists.- This optional file can contain recipe-specific instructions: extra data-seeding steps before the backup, services to check after restore, known caveats, additional verification commands, or anything else specific to testing this recipe's backup/restore cycle.
- Keep these notes in mind and apply them at the relevant steps below.
-
Fetch the recipe and check for backup labels:
- Fetch the recipe (check for local changes first — see guidelines).
- Read
~/.abra/recipes/$ARGUMENTS/compose.yml. - Search for
backupbot.backupdeploy labels. - If no backup labels are found anywhere in the compose file:
- Tell the user this recipe does not have backup configured.
- Explain what's needed: at minimum, a
backupbot.backup=truedeploy label on the main service, plus appropriate pre/post hooks for databases (see the Co-op Cloud backup spec indocs.coopcloud.tech/docs/specs/backup/). - Stop here.
- If backup labels are found, summarise the backup configuration:
- Which service has
backupbot.backup=true - Any
backupbot.backup.pre-hook/backupbot.backup.post-hookcommands - Any
backupbot.restore.pre-hook/backupbot.restore.post-hookcommands - Any volume/path restrictions (
backupbot.backup.path,backupbot.backup.volumes.*)
- Which service has
-
Ensure backup-bot-two is deployed on the server:
abra app backup createrequires backup-bot-two to be running on the server. Check by running:abra app ls --server <SERVER> --status 2>&1 | grep backup-bot- If backup-bot-two is not deployed, deploy it:
abra recipe fetch backup-bot-two --forceabra app new backup-bot-two --server <SERVER> --domain backupbot.<DOMAIN_SUFFIX> --secrets --no-inputabra app deploy backupbot.<DOMAIN_SUFFIX> --force --no-input- If
app newsays it already exists, just check it's deployed and deploy if not.
- Confirm backup-bot-two is running before proceeding.
-
Verify the app is deployed and healthy:
- Run
abra app ps <DOMAIN> --chaos --no-input -mto check deployment status (use-mfor machine-readable output to avoid TTY issues). - Run the existing health check script if available:
python3 recipe-info/$ARGUMENTS/tests/health_check.py. - If no health check script exists, curl
https://<DOMAIN>and check for HTTP 200. - If the app is not deployed, deploy it first:
abra app deploy <DOMAIN> --chaos --force --no-input, then wait and re-check. - If the backup labels were just added and the app was already deployed, redeploy so the running services pick up the new labels:
abra app deploy <DOMAIN> --chaos --force --no-input. - If
backup-test.mdspecifies any data-seeding steps to perform before the backup (e.g. creating a test document, inserting a database record), do them now.
- Run
-
Create a backup snapshot:
- First, list existing snapshots to establish a baseline:
script -qefc "abra app backup snapshots <DOMAIN> --no-input" /dev/null - Note:
backup snapshotsdoes NOT support--chaos. - Note the number of existing snapshots (may be zero).
- Run (with TTY wrapper):
script -qefc "abra app backup create <DOMAIN> --chaos --no-input" /dev/null - Confirm the output contains "backup finished" or similar success message.
- List snapshots again:
script -qefc "abra app backup snapshots <DOMAIN> --no-input" /dev/null - Confirm there is exactly one more snapshot than before.
- Note the newest snapshot ID from the output.
- First, list existing snapshots to establish a baseline:
-
Verify backup contents:
- Run
backup lsinside the backupbot container to list the files in the latest snapshot, filtered to this app:script -qefc "abra app run backupbot.<DOMAIN_SUFFIX> app -- backup -h <DOMAIN> ls" /dev/null - The
-h <DOMAIN>flag filters the listing to only this app's snapshot. Thelscommand defaults to listing under/var/lib/docker/volumes/. - Note:
abra app runrequires the TTY wrapper and does NOT support--chaos. - Save the output, then use
grepto confirm that expected files/paths are present:- Volume paths: For each volume identified as backed up in step 3, grep the output for the volume name (the Docker volume name is
<stack_name>_<volume_name>). Confirm that at least one file appears under each expected volume path. - Excluded volumes: For any volumes explicitly excluded (e.g.
backupbot.backup.volumes.<name>=false), confirm they do NOT appear in the listing. - Recipe-specific files: If
backup-test.mdspecifies particular files or patterns to look for in the backup, grep for those as well.
- Volume paths: For each volume identified as backed up in step 3, grep the output for the volume name (the Docker volume name is
- If any expected volume paths are missing or any excluded volumes appear, flag as FAIL with details about what was missing or unexpectedly present.
- Run
-
Undeploy the app:
- Run:
abra app undeploy <DOMAIN> --no-input - Undeploy does not need
--chaos.
- Run:
-
Remove volumes to simulate complete data loss:
- Run (with TTY wrapper):
script -qefc "abra app volume remove <DOMAIN> --force --no-input" /dev/null - This ensures the restore test starts from a genuinely clean slate — no leftover data.
- Do not skip this step — testing restore onto existing data is not a valid backup test.
- If volume removal fails with "volume is in use" by dead/ghost containers that Docker can't remove, clear them manually via SSH:
- Identify the ghost containers:
ssh <server> "docker ps -a --filter volume=<volume_name> --format '{{.ID}} {{.State}}'"— they will show asdead - Remove their directories:
ssh <server> "sudo rm -rf /var/lib/docker/containers/<full_container_id>" - Restart Docker:
ssh <server> "sudo systemctl restart docker" - Wait ~15 seconds, then retry volume removal
- Identify the ghost containers:
-
Redeploy from scratch:
- Run:
abra app deploy <DOMAIN> --chaos --force --no-input - Wait for the app to come up (check with
abra app ps <DOMAIN> --chaos --no-input -m). - The app may not be fully healthy yet since it has no data — that's expected.
- Run:
-
Restore from backup:
- Run (with TTY wrapper):
script -qefc "abra app restore <DOMAIN> --chaos --hooks --no-input" /dev/null - The
--hooksflag ensures restore pre/post hooks run (e.g. database import commands). - Do NOT pass
--targetto the restore command. The--targetflag is a restic restore target directory, not a stack filter — passing a stack name as--targetcauses restic to restore files into a wrong subdirectory instead of the actual volume paths, and restore hooks will fail because the files won't be where the containers expect them. - Confirm the output contains "Restoring Snapshot" or similar success message.
- Run (with TTY wrapper):
-
Redeploy the app to ensure it picks up the restored data:
- Do NOT use
abra app restart --all-services— it tends to hang on stacks with many services. - Instead, redeploy with force:
abra app deploy <DOMAIN> --chaos --force --no-input - Wait for all services to converge:
sleep 30, then check withabra app ps <DOMAIN> --chaos --no-input -m. Allow up to 60 seconds for services to start. If abra reports a deploy timeout butapp psshows all services running and healthy, treat it as a pass.
- Do NOT use
-
Run the test suite to verify the app works after restore:
- Discover and run all test scripts from
recipe-info/$ARGUMENTS/tests/*.py. - For each script, record PASS (exit 0) or FAIL (non-zero).
- Read
recipe-info/$ARGUMENTS/test.mdand perform URL-based checks usingcurlorWebFetch. - If
backup-test.mdspecifies extra verification steps (e.g. "confirm the test document still exists", "check the database has records"), perform those too. - If no tests exist at all, at minimum curl
https://<DOMAIN>and check for HTTP 200.
- Discover and run all test scripts from
-
Summarise results:
Report each phase of the backup/restore cycle:
Phase Result Backup-bot-two check PASS / FAIL Initial health check PASS / FAIL Backup creation PASS / FAIL Backup contents verification PASS / FAIL Undeploy + volume removal PASS / FAIL Fresh redeploy PASS / FAIL Restore from backup PASS / FAIL Post-restore redeploy PASS / FAIL Test suite PASS / FAIL (detail per test) - If all phases passed: confirm the recipe's backup/restore cycle is working correctly.
- If any phase failed: highlight which step failed, show relevant error output, and suggest troubleshooting:
- Check backup labels in compose.yml
- Check pre/post hook commands for errors
- Review app logs:
abra app logs <DOMAIN> - Check if all volumes are being backed up
- For database services, verify the dump/restore hooks are correct