feat(cli): add nself server for Hetzner server lifecycle (G-011) - #410
Merged
Merged
Conversation
Provisioning, resizing, and destroying a server had no CLI surface: nself access only manages SSH keys on an already-deployed host, and nself security only audits one. An operator building or tearing down a CI box tonight had to fall back to raw hcloud server create / hcloud server delete, with none of the safety a manual snapshot-and-verify procedure provides. Add nself server provision/list/resize/destroy, backed by a new internal/server package (Client interface over the Hetzner Cloud API, mockable in tests): - destroy refuses to run without --snapshot (taken and verified status=available before anything is deleted) or --force-no-backup. - destroy sets auto_delete=false on the server's primary IP(s) before deleting it, unless --release-ip is passed, and reports which IPs were retained/released. Hetzner primary IPs default to auto_delete=true, so deleting a server otherwise permanently destroys its IP too. - resize detects a disk-shrinking type change before calling the provider and explains the snapshot -> new server -> restore path, instead of surfacing Hetzner's raw invalid_input error. - the API token is resolved from --token, --token-env (defaults to the existing HETZNER_NSELF_TOKEN vault var), or HCLOUD_TOKEN — never hardcoded. +1 on the CLI-R11 command-surface budget (50 -> 51), same deliberate exception pattern used for `access` (#238): a new top-level command closing a real capability gap named in G-011, not surface creep. Regenerated the command inventory, wiki command index, and the surface-parity matrix; added .github/wiki/cmd-server.md.
…ake wiki-commands)
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes gap G-011:
nself server(provision / list / resize / destroy), backed by a newinternal/serverpackage talking to the Hetzner Cloud API. Previously an operator provisioning or destroying a server had no CLI path —nself accessonly manages SSH keys on an already-deployed host, andnself securityonly audits one — and fell back to rawhcloud server create/hcloud server delete, with none of the safety a manual snapshot-and-verify procedure gives.Design requirements -> implementation
destroyrefuses to run without a verified backup.internal/server/destroy.go'sErrNoBackupgate requires--snapshot(takes one viainternal/server/snapshot.go'sTakeVerifiedSnapshot, polling until the image reachesstatus=availablebefore returning) or an explicit--force-no-backup. A snapshot that fails or times out aborts destroy before the server is ever deleted.destroyprotects the primary IP by default.internal/server/primaryip.go'sProtectOrReleaseIPssetsauto_delete=falseon the server's primary IP(s) before deletion, unless--release-ipis passed (which explicitly setsauto_delete=trueinstead of merely "leaving it alone"). The command prints which IPs were retained vs. released, read back from what was actually set — never assumed from the flag.resizeexplains disk-shrink instead of raw-erroring.internal/server/resize.gocompares the current and target server types' disk sizes before calling the provider and returnsErrDiskShrinkwith the snapshot -> new server -> restore workaround spelled out, when Hetzner itself would only returninvalid_input.internal/server/token.go'sResolveTokenchecks--token>--token-env(defaults to the existingHETZNER_NSELF_TOKENvault var, so any other project's own token var works via--token-envwithout a code change) >HCLOUD_TOKEN.Other changes
+1on the CLI-R11 command-surface budget (50 -> 51,.github/command-surface-budget.txt), the same deliberate-exception pattern already used foraccess(nself access: manage SSH keys/users on already-deployed servers #238)..github/command-inventory.json,.github/wiki/Commands.md,.github/surface-parity.{md,json}, and SPORTF02-COMMAND-INVENTORY.md..github/wiki/cmd-server.md(T03 template).cmd/commands/groups.go+error_harness_test.go: registeredserverin the Advanced & Enterprise help group and the error-state harness.Test plan
internal/server: 35 unit tests against a hand-written fakeClient(no network) plus 4httptest.Server-backed tests proving the real Hetzner HTTP request/response wire format — covers the backup gate, IP protection, disk-shrink detection, snapshot polling (success/error/timeout), and token resolution.cmd/commands: 8 tests against a fakeserver.Clientinjected via thenewServerClientindirection (mirrorsnewAccessTransport) — no network, no real infrastructure ever touched.go build ./...,go vet ./...,gofmt -l .clean.golangci-lint runclean (fixed 3 staticcheck S1016 findings).go test ./...green, includinginternal/repoqa's command-inventory/parity/surface-budget/wiki-parity gates.