Add server hibernation#51
Conversation
|
@jmunckhof is attempting to deploy a commit to the Hayden Bleasel Team on Vercel. A member of the Team first needs to authorize it. |
|
Nice work on this — the overall shape (atomic state claims, FatalError ordering so a stuck snapshot can't nuke the VM, idempotent step keys) is solid. A few things worth tackling before this comes out of draft: Correctness
Smaller stuff
Things that read well
Test plan looks comprehensive; only thing I'd add is a cheap unit test on the action-level guards (e.g. "rejects when observedState=hibernating") since those are easy to regress. |
- Wait for VM off before snapshotting (avoids inconsistent image) - Throw FatalError on wake VM-running timeout (mirrors snapshot path) - Gate stepMarkAwake on desiredState=running so a concurrent Stop/Delete isn't clobbered - Verify existing snapshot status before reusing in stepCreateHibernationSnapshot; discard and recreate if unavailable - Pre-check VM status in stepShutdownHetzner instead of relying on a brittle 422-already-off heuristic - Release reserved IP when reserveIpOnHibernate is toggled off (delete the IP if no VM, else flip auto_delete=true so it cleans up at next teardown) - Scope hibernate-settings revalidatePath to the server page - prisma format
Resolves conflicts by taking main's provider-pattern code. The old hetzner-direct hibernate implementation is removed in this commit and will be re-added on top using the new Provider abstraction.
Reimplements hibernation on top of the Provider interface that landed in main (5c50664). Replaces direct Hetzner API calls with provider methods. Provider interface gains shutdownServer(id) for graceful ACPI shutdown before snapshotting (Hetzner warns that snapshots of running VMs may be unreadable). Hetzner adapter implements via /servers/{id}/actions/shutdown and treats 422 as already-off. Server schema gains hibernationImageId + hibernatedAt; enums extended with hibernated/hibernating/waking. Workflow steps use provider.createSnapshot, getImage, deleteImage instead of raw API calls. Teardown now also deletes any orphan hibernation snapshot so deleting a hibernated server fully releases all resources. IP-reservation is intentionally deferred to a follow-up PR.
|
Updated this on top of the provider refactor that landed in main. Pulled out the IP-reservation toggle for now Rest of the flow is the same: STOP → shutdown → snapshot → delete VM on hibernate, then create from snapshot → |
Closes #50.
Adds a hibernate / wake lifecycle so an idle game server can stop paying compute. Snapshots the disk to Hetzner image storage, deletes the VM, and restores it on demand.
Summary
desiredState:hibernated, observed stateshibernating/hibernated/wakinghibernateServer(drain agent → optional IP reserve → shutdown → snapshot → delete VM) andwakeServer(create VM from snapshot → wait for agent → drop snapshot)hibernate,wake, plus ahibernate-settingsaction to toggle "reserve IP across hibernate"emitActivityCost shape (rough)
Notes for reviewer
prisma/schema.prismachanged but I haven't generated the migration yet — happy to add one before this comes out of draftupdateManyso a double-click can't fan out two workflows; both roll back the DB if the workflowstartitself throwsFatalErrorinstead of falling through to VM deletion (so a stuck snapshot can never silently nuke the world)Out of scope
Test plan
hibernatingstate, confirm the action rejects cleanly