nspawn

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package nspawn is the isolation backend that runs the node stack in a systemd-nspawn machine. It owns every use of machinectl and systemd-run --machine so the rest of the agent stays backend neutral.

Index

Constants

View Source
const MachinesDir = "/var/lib/machines"

MachinesDir is where systemd-nspawn machine root filesystems live.

Variables

View Source
var ErrNotWired = errors.New("nspawn backend operation not configured")

ErrNotWired is returned by lifecycle operations whose implementation has not been injected into the Backend (see Ops).

View Source
var Templates = template.Must(
	template.New("nspawn").ParseFS(assets, "assets/nspawn.conf", "assets/config-regeneration.service", "assets/service-override.conf"),
)

Templates holds the nspawn settings file, the service override drop-in and the config regeneration unit, by their asset file names.

Functions

func ConfirmNotEnabled

func ConfirmNotEnabled(ctx context.Context, log *slog.Logger, service string) error

ConfirmNotEnabled reports whether the nspawn unit is definitely not enabled.

machinectl disable fails for benign reasons, most often a machine that was never enabled, so the failure alone does not justify aborting reset. It is not safe to simply ignore either: the enablement symlink outlives the config and rootfs that reset deletes, so a unit left enabled makes the host try to start a machine that no longer exists on the next boot, while reset reported success. Continue only when systemd positively reports a state that cannot start the unit at boot, and treat a failed inspection as unconfirmed.

func Enable

func Enable(ctx context.Context, r Runner, name string) error

Enable enables the machine unit so the host starts it at boot.

func Exec

func Exec(ctx context.Context, log *slog.Logger, slot string, args ...string) (string, error)

Exec runs args inside the machine named by slot using systemd-run --machine=<slot> --pipe --wait. Stdout is streamed at Debug and stderr at Error, and the captured stdout is returned.

func FirstRegistered

func FirstRegistered(ctx context.Context, log *slog.Logger, slots ...string) (string, error)

FirstRegistered returns the first of slots that is registered, or the empty string if none is. It inventories once rather than per slot, and like Registered it reports an uninspectable host as an error rather than as a clean one.

func IsAlreadyExists

func IsAlreadyExists(err error) bool

IsAlreadyExists reports whether err looks like a machinectl "machine already exists" failure (errno 17 / EEXIST surfaced as text).

func Logs

func Logs(ctx context.Context, slot string, opts isolation.LogOptions) (io.ReadCloser, error)

Logs returns the machine journal for the slot, via journalctl --machine.

func MissingTool

func MissingTool(err error) bool

MissingTool reports whether err is a host inspection tool that is not installed, as opposed to a tool that ran and failed.

func MountDeviceAllowEntries

func MountDeviceAllowEntries(mounts []config.AdditionalHostMount) []string

MountDeviceAllowEntries returns the de-duplicated, sorted DeviceAllow entries declared by mounts.

func Registered

func Registered(ctx context.Context, log *slog.Logger, name string) (bool, error)

Registered reports registration only after successful inventory. Bootstrap callers must not treat a missing inspection tool as a clean host.

func RegisteredForCleanup

func RegisteredForCleanup(ctx context.Context, log *slog.Logger, name string) (bool, error)

RegisteredForCleanup answers Registered's question with the tolerance described on MissingTool. Cleanup uses this; admission must not.

func Remove

func Remove(ctx context.Context, log *slog.Logger, name string) error

Remove removes the machine rootfs using machinectl and falls back to force-removing the directory once machined no longer knows the machine.

func RemoveBPFFSMount

func RemoveBPFFSMount(ctx context.Context, log *slog.Logger, mountPath string) error

RemoveBPFFSMount unmounts and removes the machine's private bpffs mount.

func RemoveConfig

func RemoveConfig(log *slog.Logger, p ConfigPaths) error

RemoveConfig removes the nspawn settings file, the service override directory and the config regeneration unit.

func Root

func Root(slot string) isolation.DirRoot

Root returns the NodeRoot for the machine slot, rooted at its directory under MachinesDir.

func RootAt

func RootAt(slot, dir string) isolation.DirRoot

RootAt returns the NodeRoot for slot rooted at dir.

func ServiceName

func ServiceName(machine string) string

ServiceName is the systemd unit that runs the machine.

func StartWithRecovery

func StartWithRecovery(ctx context.Context, log *slog.Logger, r Runner, name string) (wasRunning bool, err error)

StartWithRecovery runs `machinectl start` and, if it fails because the machine is already registered (for example systemd-machined was restarted out from under us, leaving an orphaned registration with errno 17 / "File exists"), terminates the stale registration and retries once. It reports whether the machine was already running before the call, in which case nothing was started.

func Stop

func Stop(ctx context.Context, log *slog.Logger, name string) error

Stop stops the machine: it disables the unit, stops the service, waits up to 30 seconds for the machine to unregister, and force-terminates it if necessary. A host without machinectl has nothing to stop.

func WaitForMachine

func WaitForMachine(ctx context.Context, log *slog.Logger, machine string) error

WaitForMachine polls the machine until it is responsive to systemd-run commands. machinectl start returns before D-Bus is ready, so phases that use Exec would fail without this gate.

Types

type Backend

type Backend struct {
	Log *slog.Logger
	Ops Ops
}

Backend is the systemd-nspawn isolation backend.

func (*Backend) Destroy

func (b *Backend) Destroy(ctx context.Context, r isolation.SlotRef) error

Destroy implements isolation.Backend.

func (*Backend) Exec

Exec implements isolation.Backend.

func (*Backend) Logs

Logs implements isolation.Backend.

func (*Backend) Mode

func (*Backend) Mode() isolation.Mode

Mode implements isolation.Backend.

func (*Backend) Prepare

Prepare implements isolation.Backend. nspawn derives the machine's devices, mounts and libraries from the host when the machine is built, not from NodeSpec.Grants, so a request for grants fails rather than being dropped.

func (*Backend) Requirements

func (*Backend) Requirements() isolation.Requirements

Requirements implements isolation.Backend.

func (*Backend) Restart

func (b *Backend) Restart(ctx context.Context, r isolation.SlotRef) error

Restart implements isolation.Backend.

func (*Backend) Start

func (b *Backend) Start(ctx context.Context, r isolation.SlotRef) error

Start implements isolation.Backend.

func (*Backend) Status

Status implements isolation.Backend. The machine is running when a command can be executed inside it.

func (*Backend) Stop

func (b *Backend) Stop(ctx context.Context, r isolation.SlotRef) error

Stop implements isolation.Backend.

type Bind

type Bind struct {
	// Source is the host path passed to Bind=/BindReadOnly=.
	Source string
	// Target is the optional container path. Empty means "bind at Source".
	Target string
	// ReadOnly controls whether BindReadOnly= is used instead of Bind=.
	ReadOnly bool
}

Bind describes one Bind= or BindReadOnly= entry of the .nspawn file.

type ConfigPaths

type ConfigPaths struct {
	// NSpawnFile is the .nspawn settings file.
	NSpawnFile string
	// OverrideDir is the systemd-nspawn@<machine>.service.d drop-in directory.
	OverrideDir string
	// ConfigRegenerationUnit is the config regeneration unit file.
	ConfigRegenerationUnit string
	// BPFFSMountPath is the machine's private bpffs mount point.
	BPFFSMountPath string
}

ConfigPaths are the host files the backend renders for one machine.

type DeviceAllow

type DeviceAllow struct {
	// Specifier is the systemd DeviceAllow selector (path or char-* class).
	Specifier string
	// Access is the device permission mode, usually "rwm".
	Access string
}

DeviceAllow describes one DeviceAllow= entry of the service override.

type DeviceTarget

type DeviceTarget struct {
	// Bind controls visibility of a host path inside nspawn.
	Bind Bind
	// Allow controls cgroup device access for the corresponding target.
	Allow DeviceAllow
}

DeviceTarget pairs the bind that makes a device visible with the cgroup rule that makes it usable.

func NvidiaDeviceTargets

func NvidiaDeviceTargets(paths []string) []DeviceTarget

NvidiaDeviceTargets returns the bind and DeviceAllow pair for each NVIDIA device path.

type Ops

type Ops struct {
	Prepare func(ctx context.Context, n isolation.NodeSpec) (isolation.NodeRoot, error)
	Start   func(ctx context.Context, r isolation.SlotRef) error
	Stop    func(ctx context.Context, r isolation.SlotRef) error
	Restart func(ctx context.Context, r isolation.SlotRef) error
	Destroy func(ctx context.Context, r isolation.SlotRef) error
}

Ops are the lifecycle operations the backend delegates to. They live in the phase packages, which depend on this package, so they are injected by the composition root rather than imported. A nil Ops field makes the matching Backend method return ErrNotWired.

type Rendered

type Rendered struct {
	// Settings is the .nspawn file.
	Settings []byte
	// ServiceOverride is the systemd-nspawn@<machine>.service drop-in.
	ServiceOverride []byte
	// ConfigRegenerationUnit is the unit that regenerates machine config.
	ConfigRegenerationUnit []byte
}

Rendered is the host configuration the backend writes for one machine.

func Render

func Render(data TemplateData) (Rendered, error)

Render executes the three templates for data.

type Runner

type Runner interface {
	Start(ctx context.Context, name string) error
	Enable(ctx context.Context, name string) error
	Terminate(ctx context.Context, name string) error
	Exists(ctx context.Context, name string) bool
	ResetFailed(ctx context.Context, name string) error
	Running(ctx context.Context, name string) (bool, error)
}

Runner abstracts the small set of machinectl and systemctl operations the backend needs, so the recovery paths can be exercised without invoking real binaries. NewRunner returns the implementation that shells out via executil.

func NewRunner

func NewRunner(log *slog.Logger) Runner

NewRunner returns the Runner that drives the real machinectl and systemctl.

type TemplateData

type TemplateData struct {
	// MachineName is the nspawn machine name (e.g. "kube1"). Used by the
	// service drop-in for the ExecStartPre `machinectl terminate` cleanup.
	MachineName                  string
	BPFFSMountPath               string
	ContainerImageArchiveDir     string
	ContainerImageArchiveHostDir string
	HostDevicePaths              []string
	HostDeviceGroupSpecifiers    []string
	// MountDeviceAllow holds DeviceAllow entries (paths or device groups)
	// declared by host mounts, such as whole device directories.
	MountDeviceAllow     []string
	AdditionalHostMounts []config.AdditionalHostMount
	// NvidiaDeviceTargets contains render-ready Bind+DeviceAllow pairs.
	NvidiaDeviceTargets    []DeviceTarget
	NvidiaLibDirMounts     []isolation.LibDirMount
	NvidiaI386LibDirMounts []isolation.LibDirMount
	NvidiaBinDir           string
	AMDGPUDevicePaths      []string
	AMDSysFSPaths          []string
	ConfigRegenerationUnit string
	// PreStartCommand and PostStartCommand are the lifecycle hooks' commands.
	PreStartCommand  string
	PostStartCommand string
}

TemplateData holds the data passed to the nspawn.conf, service-override.conf and config-regeneration.service templates. Using a struct (rather than map[string]any) lets the templates call helper methods directly.

func (*TemplateData) ApplyGrants

func (d *TemplateData) ApplyGrants(grants []isolation.Grant)

ApplyGrants fills the device, mount and library fields of d from backend neutral grants. This is how the nspawn backend applies grants: mounts become Bind= and BindReadOnly= lines and devices become Bind= plus DeviceAllow= lines. A mount's DeviceAllow entries become DeviceAllow= lines. The order of grants within a purpose is preserved.

Jump to

Keyboard shortcuts

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