server

package
v1.3.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2026 License: MIT Imports: 9 Imported by: 0

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

View Source
const DefaultSnapshotWait = 10 * time.Minute

DefaultSnapshotWait is used by the command layer when --snapshot is passed without an explicit timeout.

View Source
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.

View Source
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.

View Source
const ManagedByValue = "nself-cli"

ManagedByValue is ManagedByLabel's value on every server Provision creates.

Variables

View Source
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.

View Source
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

func ResolveToken(explicit, envVar string) (string, error)

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).

func Resize

func Resize(ctx context.Context, client Client, req ResizeRequest) (*Action, error)

Resize changes req.ServerID to req.TargetType. Before calling the provider, it fetches the server's current type and req.TargetType's specs and refuses (with ErrDiskShrink) if the target has a smaller disk than the server currently has.

type ActionError

type ActionError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

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

func NewHetznerClient(token string) Client

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

type DestroyResult struct {
	SnapshotID  int64
	RetainedIPs []PrimaryIP
	ReleasedIPs []PrimaryIP
}

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

type ResizeRequest struct {
	ServerID    int64
	TargetType  string
	UpgradeDisk bool
}

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.

func List

func List(ctx context.Context, client Client, opts ListOptions) ([]Server, error)

List returns every server visible to client, optionally filtered by opts.LabelSelector.

func Provision

func Provision(ctx context.Context, client Client, req ProvisionRequest) (*Server, error)

Provision validates req and creates the server through client.

type ServerType

type ServerType struct {
	Name   string  `json:"name"`
	Cores  int     `json:"cores"`
	Memory float64 `json:"memory"`
	Disk   int     `json:"disk"` // GB
}

ServerType describes a Hetzner server type's capacity, used by Resize to detect a disk-shrink request before ever calling the provider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL