Documentation
¶
Overview ¶
Package server implements `nself server` (G-011): provisioning, listing, resizing, and destroying cloud servers, backed by the Hetzner Cloud API.
Purpose: shared data types for the server lifecycle. Kept separate from client.go so the wire-format structs (json tags) are easy to audit independent of transport/HTTP concerns. Inputs: none (pure type definitions). Outputs: none. Constraints: field sets are intentionally narrow — only what `nself server` itself needs, not a full Hetzner API mirror.
Index ¶
Constants ¶
const DefaultSnapshotWait = 10 * time.Minute
DefaultSnapshotWait is used by the command layer when --snapshot is passed without an explicit timeout.
const DefaultTokenEnvVar = "HETZNER_NSELF_TOKEN"
DefaultTokenEnvVar is the vault-documented env var for this repo's own Hetzner project. Other ~/Sites projects pass their own var name via --token-env (e.g. HETZNER_UNYECO_TOKEN) rather than needing a code change.
const ManagedByLabel = "managed-by"
ManagedByLabel marks every server this package creates, so `nself server list` (and any future audit) can distinguish nself-created boxes from anything else living in the same Hetzner project.
const ManagedByValue = "nself-cli"
ManagedByValue is ManagedByLabel's value on every server Provision creates.
Variables ¶
var ErrDiskShrink = errors.New("disk shrink is not supported by Hetzner Cloud")
ErrDiskShrink is returned (wrapped, via errors.Is-compatible %w) when a resize would shrink the server's disk. Hetzner Cloud has no API to do this at all — it is a hard provider limitation, not a permission or quota issue — so this is never retried internally.
var ErrNoBackup = errors.New("destroy refused: no verified backup — pass --snapshot to take one first, or --force-no-backup to proceed without one")
ErrNoBackup is returned when destroy is invoked with neither --snapshot nor --force-no-backup. This is the design-requirement-1 gate: a destroy call may never reach the provider's DELETE endpoint without one of these being explicit.
Functions ¶
func ResolveToken ¶
ResolveToken returns the Hetzner API token to use, in priority order: explicit (the --token flag) > envVar (--token-env, or DefaultTokenEnvVar if empty) > legacyTokenEnvVar. It never reads a token from a file or hardcodes one — vault.env is expected to be sourced into the process environment before nself runs, per this repo's credential doctrine.
Types ¶
type Action ¶
type Action struct {
ID int64 `json:"id"`
Status string `json:"status"` // "running" | "success" | "error"
Command string `json:"command"`
Error *ActionError `json:"error,omitempty"`
Progress int `json:"progress"`
}
Action is a Hetzner async action (used to poll snapshot creation).
type ActionError ¶
ActionError is the error payload embedded in a failed Action.
type Client ¶
type Client interface {
CreateServer(ctx context.Context, req ProvisionRequest) (*Server, error)
ListServers(ctx context.Context, opts ListOptions) ([]Server, error)
GetServer(ctx context.Context, id int64) (*Server, error)
DeleteServer(ctx context.Context, id int64) error
ListServerTypes(ctx context.Context) ([]ServerType, error)
ChangeServerType(ctx context.Context, serverID int64, targetType string, upgradeDisk bool) (*Action, error)
CreateSnapshot(ctx context.Context, serverID int64, description string) (*Image, *Action, error)
GetImage(ctx context.Context, id int64) (*Image, error)
GetAction(ctx context.Context, id int64) (*Action, error)
ListPrimaryIPs(ctx context.Context, serverID int64) ([]PrimaryIP, error)
SetPrimaryIPAutoDelete(ctx context.Context, ipID int64, autoDelete bool) error
}
Client is every Hetzner Cloud operation `nself server` needs. Introducing a second provider later means adding a second implementation of this interface, not touching provision.go/resize.go/destroy.go/list.go.
func NewHetznerClient ¶
NewHetznerClient builds a Client backed by the live Hetzner Cloud API. token must be non-empty; resolve it with ResolveToken before calling this.
type DestroyRequest ¶
type DestroyRequest struct {
ServerID int64
TakeSnapshot bool
ForceNoBackup bool
ReleaseIP bool
SnapshotWait time.Duration
}
DestroyRequest describes a destroy request and the safety choices made by the operator invoking it.
type DestroyResult ¶
DestroyResult reports what a Destroy call actually did, so the command layer can print an accurate summary (never assume from the request alone — e.g. a server with no primary IPs retains none).
func Destroy ¶
func Destroy(ctx context.Context, client Client, req DestroyRequest) (*DestroyResult, error)
Destroy validates req, optionally takes and verifies a snapshot, protects (or releases) the server's primary IPs, and only then deletes the server.
type Image ¶
type Image struct {
ID int64 `json:"id"`
Type string `json:"type"` // "snapshot", "backup", "system", ...
Status string `json:"status"` // "creating" | "available"
Description string `json:"description"`
}
Image is a Hetzner image/snapshot resource.
func TakeVerifiedSnapshot ¶
func TakeVerifiedSnapshot(ctx context.Context, client Client, serverID int64, description string, wait time.Duration) (*Image, error)
TakeVerifiedSnapshot creates a snapshot of serverID and blocks until Hetzner reports the resulting image as status=available. It returns as soon as either condition is met: success (image available), the underlying action reports status=error, or wait elapses.
type ListOptions ¶
type ListOptions struct {
LabelSelector string
}
ListOptions filters `nself server list`.
type PrimaryIP ¶
type PrimaryIP struct {
ID int64 `json:"id"`
IP string `json:"ip"`
Type string `json:"type"` // "ipv4" | "ipv6"
AssigneeID int64 `json:"assignee_id"`
AutoDelete bool `json:"auto_delete"`
}
PrimaryIP is a Hetzner primary IP resource. AutoDelete controls whether the IP is destroyed along with its assigned server — the exact footgun design requirement 2 exists to close.
func ProtectOrReleaseIPs ¶
func ProtectOrReleaseIPs(ctx context.Context, client Client, serverID int64, release bool) (retained, released []PrimaryIP, err error)
ProtectOrReleaseIPs reads serverID's primary IPs and, unless release is true, sets auto_delete=false on each so the upcoming server deletion does not take the IP with it. When release is true, it explicitly ensures auto_delete=true (Hetzner's default) so the IP is freed along with the server, and does so as a real, deliberate write rather than "leave it alone" — protecting a previous accidental protect-then-release cycle from ever landing on a false negative.
type ProvisionRequest ¶
type ProvisionRequest struct {
Name string
ServerType string
Location string
Image string
SSHKeys []string
Labels map[string]string
UserData string
}
ProvisionRequest describes a server to create.
type ResizeRequest ¶
ResizeRequest describes a resize (change_type) request.
type Server ¶
type Server struct {
ID int64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
ServerType string `json:"server_type"`
Location string `json:"location"`
Created time.Time `json:"created"`
IPv4 string `json:"ipv4"`
IPv6 string `json:"ipv6"`
IPv4ID int64 `json:"ipv4_id,omitempty"`
IPv6ID int64 `json:"ipv6_id,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
Server is the subset of a Hetzner Cloud server this package cares about.