hetzner

package
v0.26.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: AGPL-3.0 Imports: 46 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewHetznerCloudProvider

func NewHetznerCloudProvider() cloud.CloudProvider

func WaitForDNSResolution

func WaitForDNSResolution(ctx context.Context, fqdns []string, expectedIP string) error

WaitForDNSResolution blocks until every fqdn in fqdns resolves to expectedIP through the OS resolver, ctx is cancelled, or dnsTotalTimeout passes (the wait fails closed — interactive bootstrap can't loop forever on a missing record).

No skip option: bypassing the wait would just push the failure into a later stage (cert-manager's first ACME HTTP-01, NetBird OIDC callback, etc.) where the symptom is harder to diagnose. Better to fail here with a clear "DNS records still not resolving" + cache- flush hint than to half-bootstrap and debug from a downstream pod's 503.

func WaitForIngressLBDNS

func WaitForIngressLBDNS(ctx context.Context, clusterClient client.Client) error

WaitForIngressLBDNS gates bootstrap on the operator pointing the public-facing FQDNs (keycloak.dns / netbird.dns / netbird.stunDNS / netbird.turnDNS) at the Traefik LB's public IP. Run as SyncAllArgoCDApps's beforeRemainingApps gate — after ccm + traefik are synced (so the LB is being provisioned) but before the application-layer apps (netbird, keycloakx) whose Ingress certificates depend on DNS resolving. We poll the Service for status.loadBalancer.ingress[0].ip and only then prompt the operator.

cert-manager's ACME challenges for keycloakx / netbird Ingresses retry with exponential backoff. Putting the DNS prompt here lets the next retry succeed instead of leaving the apps unhealthy while the operator scrambles to figure out which IP to point at.

No-op when no FQDNs are configured (workload clusters, or VPN clusters with no Keycloak/NetBird DNS set).

Types

type ActivateHRobotLinuxInstallationResponseBody

type ActivateHRobotLinuxInstallationResponseBody struct {
	Linux struct {
		Dist          string   `json:"dist"`
		Lang          string   `json:"lang"`
		Password      string   `json:"password"`
		AuthorizedKey []string `json:"authorized_key"`
		Active        bool     `json:"active"`
	} `json:"linux"`
}

type CreateVSwitchResponseBody

type CreateVSwitchResponseBody struct {
	ID int `json:"id"`

	Name   string `json:"name"`
	VLANID int    `json:"vlan"`

	Cancelled bool `json:"cancelled"`
}

type FailoverIPDetails

type FailoverIPDetails struct {
	ActiveServerIP string `json:"active_server_ip"`
}

type GetFailoverIPDetailsResponse

type GetFailoverIPDetailsResponse struct {
	Failover FailoverIPDetails `json:"failover"`
}

type GetKeysResponse

type GetKeysResponse []struct {
	Key Key `json:"key"`
}

type GetServerResponseBody

type GetServerResponseBody struct {
	Server Server `json:"server"`
}

type HCloudMachineTemplateUpdates

type HCloudMachineTemplateUpdates struct {
	NewImageName string
}

type HRobotResetResponseBody

type HRobotResetResponseBody struct {
	Reset struct {
		ServerIP string `json:"server_ip"`
		Type     string `json:"type"`
	} `json:"reset"`
}

type Hetzner

type Hetzner struct {
	// contains filtered or unexported fields
}

func (*Hetzner) AttachHCloudServerToNetwork

func (h *Hetzner) AttachHCloudServerToNetwork(ctx context.Context, serverID, networkID int) error

AttachHCloudServerToNetwork attaches the given HCloud server to the given Hetzner Network.

func (*Hetzner) AttachServerToVSwitch

func (h *Hetzner) AttachServerToVSwitch(ctx context.Context, serverID string, vswitchID int) error

func (*Hetzner) ConnectVSwitchWithHetznerNetwork

func (h *Hetzner) ConnectVSwitchWithHetznerNetwork(ctx context.Context, network *hcloud.Network) error

When using Hetzner Bare Metal, we need to establish private connectivity between them and the HCloud servers in a Hetzner Network, using a VSwitch.

func (*Hetzner) CreateHCloudSSHKey

func (h *Hetzner) CreateHCloudSSHKey(ctx context.Context, name string, sshKeyPair config.SSHKeyPairConfig) error

CreateHCloudSSHKey creates the given SSH key in HCloud, if it doesn't already exist.

When an entry with the same fingerprint already exists under a different name (operator's personal yubikey re-registered as "<cluster>" via a prior run, or a hand-onboarded "ashish-laptop" entry), the existing entry is reused — Hetzner authenticates by key material, not label. The in-memory cfg.SSHKeyPair.Name is updated to match the existing entry so downstream readers (CAPH chart values, the SealedSecret, NAT-gateway server creation) all look up the right name.

func (*Hetzner) CreateHetznerBareMetalSSHKey

func (h *Hetzner) CreateHetznerBareMetalSSHKey(
	ctx context.Context,
	name string,
	sshKeyPair config.SSHKeyPairConfig,
) error

CreateHetznerBareMetalSSHKey creates the given SSH key in Hetzner Bare Metal, if it doesn't already exist.

Same idempotency contract as CreateHCloudSSHKey: an entry with the same fingerprint under a different name (typical when the operator's SSH key has been hand-onboarded into Robot or registered by an earlier cluster) is reused, and cfg.SSHKeyPair.Name is updated to match so downstream chart values + SealedSecret render the right name.

func (*Hetzner) CreateLB

func (h *Hetzner) CreateLB(ctx context.Context,
	clusterName string,
	network *hcloud.Network,
	location string,
	enablePublicInterface bool,
) (*hcloud.LoadBalancer, error)

CreateLB creates the Hetzner control-plane LB if it doesn't already exist. enablePublicInterface controls whether the LB carries a public IPv4 — true during bootstrap (NetBird isn't routing the private subnet yet), false in steady state.

func (*Hetzner) CreateNATGateway

func (h *Hetzner) CreateNATGateway(ctx context.Context, networkID int) error

func (*Hetzner) CreateNetwork

func (h *Hetzner) CreateNetwork(ctx context.Context) (*hcloud.Network, error)

CreateNetwork creates the Hetzner Network, if it doesn't already exist.

func (*Hetzner) CreateVSwitch

func (h *Hetzner) CreateVSwitch(ctx context.Context) (int, error)

A VSwitch is used to establish private connectivity between Hetzner Bare Metal servers (and a Hetzner Network, when spinning up a Hetzner hybrid cluster). This function is responsible for creating that VSwitch, if it doesn't already exist. The VSwitch ID gets returned.

func (*Hetzner) DisableControlPlaneLBPublicInterface

func (h *Hetzner) DisableControlPlaneLBPublicInterface(ctx context.Context) error

DisableControlPlaneLBPublicInterface is the post-bootstrap finalize call: clusters whose control-plane LB ran with a public interface during bootstrap (so kubeadm join, ArgoCD setup, etc. could reach the apiserver before NetBird was up) flip it off here, leaving the LB reachable only through its private IP via the NetBird mesh.

Applies to the two modes that pre-create the LB during bootstrap:

  • VPN clusters: this cluster IS the VPN, bootstraps behind a public LB and transitions to private-via-NetBird once netbird itself is Healthy. Until v1.x of this code the guard skipped these because HCloudVPNCluster is nil for the VPN itself, which left the control-plane public on a freshly bootstrapped VPN cluster — fixed by including cluster.type=vpn in the gate.

  • workload clusters connecting to an existing VPN: same public- during-join / private-after-NetBird lifecycle, signaled by a non-nil HCloudVPNCluster pointing at the parent VPN.

Other modes (bare-metal CP, plain workload without a parent VPN) never had a public HCloud LB to disable — early return.

Also gated on hostname being configured: without an FQDN the LB's public IP is the operator's only entry point to kube-apiserver, and disabling it would lock them out of their own cluster.

func (*Hetzner) DisableDeletionProtection

func (h *Hetzner) DisableDeletionProtection(ctx context.Context) error

DisableDeletionProtection disables deletion protection on the KubeAPI LB and NAT Gateway server so that CAPH can delete them during cluster teardown.

func (*Hetzner) GenerateStoragePlans

func (h *Hetzner) GenerateStoragePlans(ctx context.Context, hetznerConfig *config.HetznerConfig) error

func (*Hetzner) GetHCloudServerIDsForCluster

func (h *Hetzner) GetHCloudServerIDsForCluster(ctx context.Context, name string) ([]int, error)

GetHCloudServerIDsForCluster returns IDs of the HCloud servers associated with the given Kubernetes cluster which was provisioned using Cluster API Provider Hetzner (CAPH).

func (*Hetzner) GetHetznerBareMetalHostPublicIPs added in v0.26.0

func (h *Hetzner) GetHetznerBareMetalHostPublicIPs(ctx context.Context) (map[string]string, error)

GetHetznerBareMetalHostPublicIPs returns a map of HetznerBareMetalHost ServerID to the Robot main IP for that server. Used by the kubelet-csr- approver values template to widen providerIpPrefixes with one /32 per node (kubelet's serving-cert SAN list always contains every local interface IP — both vSwitch and public — so the approver's allow-list has to cover both or every CSR is denied).

Returns an empty map on non-bare-metal Hetzner setups; the matching values template guards on map presence and skips iteration. Robot API calls are sequential (small N, Robot's per-account rate limit is unforgiving).

Follow-up removal: once https://github.com/syself/cluster-api-provider-hetzner/issues/2095 lands and the CAPH dep is bumped to a version that includes the fix, the kubelet-csr-approver chart + this helper + its callers can all be deleted (CAPH's native CSR validator is structurally tighter than postfinance's IP-prefix allow-list).

func (*Hetzner) GetVMSpecs

func (h *Hetzner) GetVMSpecs(ctx context.Context, machineType string) (*cloud.VMSpec, error)

func (*Hetzner) InstallOSOnAllHBMS

func (h *Hetzner) InstallOSOnAllHBMS(ctx context.Context) error

InstallOSOnAllHBMS installs Ubuntu on each HBMS in parallel. HBMS that are already SSH-reachable are skipped (idempotency). Since each HBMS's OS installation takes ~8-12 minutes regardless of others, processing them in parallel bounds total wall-clock time to that of the slowest single host.

func (*Hetzner) PointFailoverIPToInitMasterNode

func (h *Hetzner) PointFailoverIPToInitMasterNode(ctx context.Context) error

func (*Hetzner) ProvisionPrerequisiteInfrastructure

func (h *Hetzner) ProvisionPrerequisiteInfrastructure(ctx context.Context) error

ProvisionPrerequisiteInfrastructure provisions infrastructure required before CAPH starts spinning up the cluster.

func (*Hetzner) SetControlPlaneLBPublicInterface

func (h *Hetzner) SetControlPlaneLBPublicInterface(
	ctx context.Context, clusterName string, enabled bool,
) (*hcloud.LoadBalancer, error)

SetControlPlaneLBPublicInterface ensures the LB's public interface matches enabled. Idempotent — no-op when already in the desired state. Used both during bootstrap (enable) and post-bootstrap finalize (disable) once NetBird routes the private endpoint.

func (*Hetzner) SetupDisasterRecovery

func (*Hetzner) SetupDisasterRecovery(_ context.Context) error

func (*Hetzner) UpdateCapiClusterValuesFile

func (*Hetzner) UpdateCapiClusterValuesFile(ctx context.Context, path string, updates any) error

func (*Hetzner) UpdateMachineTemplate

func (*Hetzner) UpdateMachineTemplate(ctx context.Context,
	clusterClient client.Client,
	name string,
	updates any,
) error

type HetznerBareMetalMachineTemplateUpdates

type HetznerBareMetalMachineTemplateUpdates struct {
	NewImagePath string
}

type Key

type Key struct {
	Name        string `json:"name"`
	Fingerprint string `json:"fingerprint"`
}

type ListVSwitchResponseBody

type ListVSwitchResponseBody = []struct {
	ID int `json:"id"`

	Name   string `json:"name"`
	VLANID int    `json:"vlan"`

	Cancelled bool `json:"cancelled"`
}

type Server

type Server struct {
	IP string `json:"server_ip"`
}

Jump to

Keyboard shortcuts

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