feat(databases): manage database backups and restores from the CLI - #168
feat(databases): manage database backups and restores from the CLI#168subnetmarco wants to merge 14 commits into
Conversation
Adds `backups list|create|get|delete`, `restore` by backup name or timestamp, and `backup-schedule get|set` under the cloud tree. The commands are left out of local mode, where there is no provider to back a database up with. Co-authored-by: Cursor <cursoragent@cursor.com>
The CLI surfaces the API's 409 as-is, so the commands that wait for a restore were only discoverable by hitting one. Co-authored-by: Cursor <cursoragent@cursor.com>
Backups, restores, and branches need the storage provider, which local development does not run, so the local command tree leaves them out. Cobra answers a subcommand a tree does not have by printing the parent's help and exiting 0, so `volcano databases backups list app` looked like it worked and said nothing about the command living under `volcano cloud`. Hidden stubs answer those names with the cloud path instead, asserted in the command tests and against the running stack in the local-mode E2E. Sync the vendored spec with the platform's four new backup `409`s, pass them to the error mapper so a restore-in-flight or still-provisioning database keeps its message, and cover the paths that had none: a duplicate create, a missing backup, a delete refused by a running restore, a monthly schedule, and an empty `--to`. Document `--yes`, the per-frequency `--day`, and how to run the cloud API E2E, which no CI job does. Co-authored-by: Cursor <cursoragent@cursor.com>
Starting a restore printed an id and then pointed at `databases get`, which only ever reports that the database is restoring. When a restore failed the reason was on the restore record, reachable from the API and the dashboard but from nothing in the CLI, so a failed restore read as a database that had gone `failed` for no stated reason. Adds `databases restores list` and `databases restores get`, which the restore command now names with the id it just printed, and which say what the status means for the database rather than leaving it to be inferred. The local tree turns both away with the cloud path, like the rest of the provider-backed commands, and the local-mode smoke test covers the aliases and `restore --to` it was missing. Co-authored-by: Cursor <cursoragent@cursor.com>
Backups are a paid capability now, and the plan refusal reaches the reads too, so a 403 on `backups list` is the expected answer rather than a token problem. Say so up front and cover it in the command tests, which is what keeps the CLI passing the reason through instead of the status. Co-authored-by: Cursor <cursoragent@cursor.com>
Backup storage counts against the parent database's storage allowance, and the Size column is what a backup holds rather than what it costs to keep, so a schedule's backups read larger than they are billed. Co-authored-by: Cursor <cursoragent@cursor.com>
The stats endpoint now reports `backup_storage_bytes` and folds it into `current_storage_bytes`, and a database's `storage_bytes` gauge carries it too. Co-authored-by: Cursor <cursoragent@cursor.com>
The list's storage_bytes is written by a background pass, so it can trail the stats endpoint's current_storage_bytes by more than the few minutes the backup figure's own wording promises. Co-authored-by: Cursor <cursoragent@cursor.com>
…s answer Co-authored-by: Cursor <cursoragent@cursor.com>
The API numbers the week from Monday: 1 to 7, with no zero. --day accepted 0 to 6, so every weekday named the one before it, Sunday could not be asked for, and a weekly schedule with the flag left off sent a day the API refuses. It is required for a weekly schedule now, and the schedule the CLI prints back names the day the API meant. Co-authored-by: Cursor <cursoragent@cursor.com>
…ers to The guard is what turns an unknown local subcommand from cobra's help-and-exit-0 into an error, so a spelling it does not answer to reads as success. Nothing covered `branch` or `restore-history`, and the local-mode E2E never invoked branches at all despite checking that local help hides them. The guard also has to name a command the cloud tree answers to, which is now asserted rather than assumed. Co-authored-by: Cursor <cursoragent@cursor.com>
A branch shares its parent's data, not its credentials. The cloud E2E rotated a branch's password and only checked the command reported success, so a rotation that reached the parent's connection string would have gone unnoticed. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
The CLI-specific findings are attached inline.
There is also a cross-repository compatibility concern in Kong/volcano-hosting#880: its public docs guarantee that a backup schedule survives restore, but carryBackupSchedule and moveSchedule swallow provider read/write failures and let repair continue (https://github.com/Kong/volcano-hosting/blob/3f861693feeac1149420294f5fc1b54c6e11e14c/internal/databasebackups/repair.go#L89-L130). A transient failure can therefore complete the restore with no schedule on the restored branch, while the orphan may keep producing billed snapshots.
Otherwise, the five route shapes, request bodies, response structures, status codes, retention units, and weekday numbering are compatible across CLI #168, Hosting #880, and Web #522. I verified CLI head 905cf97e with go test ./..., lint, and generated-client checks; focused Hosting backup/restore tests passed at 3f861693.
An exhausted restore does not always leave the database failed. A backup that no longer exists at the provider ends the restore before anything is replaced, and the database goes back to active untouched — so telling the user their data "is whatever the last attempt left behind" was wrong in exactly the case that is most alarming and least deserved. The note now says Volcano gave up and what each outcome means, and the vendored contract and the docs carry the same wording as the API. Also says the restore list is the 50 most recent rather than every restore, which is what the endpoint returns. Co-authored-by: Cursor <cursoragent@cursor.com>
Volcano retries a restore attempt on its own and the restore reads `pending` in between, so `failed` and `exhausted` are the same news: it is done trying. The note told the reader another attempt was coming, which left them waiting on nothing. Co-authored-by: Cursor <cursoragent@cursor.com>
The CLI half of database backups. Depends on Kong/volcano-hosting#880, which
adds the routes; the dashboard side is Kong/volcano-web#522.
Summary
backups list|create|get|delete, following the branch commands' shape andtable output.
restoreby backup name (--backup) or by timestamp inside the plan'swindow (
--to). It queues the restore and reports the id; the database isrestoringuntil the platform finishes, and its connection string does notchange.
restores list|get, so the idrestoreprints is worth having: thestatus, how far it got, and why it stopped if it did.
listshows the 50 mostrecent, which is what the endpoint returns.
backup-schedule get|setfor the provider-side daily, weekly, or monthlyschedule (
--frequency,--hour,--day,--retention,--clear) and itsretention.
with, guarded by a test rather than by convention.
API's
409as-is, and that backups need Pro — the plan refusal reaches thereads too, so a
403onbackups listis the plan talking, not the token.Test plan
go test ./...make lint,make openapi-generated-checktests/e2e/api/database_backups_test.goagainst a deployed environmentonce Kong/volcano-hosting#880 is out.
Made with Cursor