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
- Variables
- func ConfirmNotEnabled(ctx context.Context, log *slog.Logger, service string) error
- func Enable(ctx context.Context, r Runner, name string) error
- func Exec(ctx context.Context, log *slog.Logger, slot string, args ...string) (string, error)
- func FirstRegistered(ctx context.Context, log *slog.Logger, slots ...string) (string, error)
- func IsAlreadyExists(err error) bool
- func Logs(ctx context.Context, slot string, opts isolation.LogOptions) (io.ReadCloser, error)
- func MissingTool(err error) bool
- func MountDeviceAllowEntries(mounts []config.AdditionalHostMount) []string
- func Registered(ctx context.Context, log *slog.Logger, name string) (bool, error)
- func RegisteredForCleanup(ctx context.Context, log *slog.Logger, name string) (bool, error)
- func Remove(ctx context.Context, log *slog.Logger, name string) error
- func RemoveBPFFSMount(ctx context.Context, log *slog.Logger, mountPath string) error
- func RemoveConfig(log *slog.Logger, p ConfigPaths) error
- func Root(slot string) isolation.DirRoot
- func RootAt(slot, dir string) isolation.DirRoot
- func ServiceName(machine string) string
- func StartWithRecovery(ctx context.Context, log *slog.Logger, r Runner, name string) (wasRunning bool, err error)
- func Stop(ctx context.Context, log *slog.Logger, name string) error
- func WaitForMachine(ctx context.Context, log *slog.Logger, machine string) error
- type Backend
- func (b *Backend) Destroy(ctx context.Context, r isolation.SlotRef) error
- func (b *Backend) Exec(ctx context.Context, r isolation.SlotRef, cmd isolation.ExecSpec) (isolation.ExecResult, error)
- func (*Backend) Logs(ctx context.Context, r isolation.SlotRef, opts isolation.LogOptions) (io.ReadCloser, error)
- func (*Backend) Mode() isolation.Mode
- func (b *Backend) Prepare(ctx context.Context, n isolation.NodeSpec) (isolation.NodeRoot, error)
- func (*Backend) Requirements() isolation.Requirements
- func (b *Backend) Restart(ctx context.Context, r isolation.SlotRef) error
- func (b *Backend) Start(ctx context.Context, r isolation.SlotRef) error
- func (b *Backend) Status(ctx context.Context, r isolation.SlotRef) (isolation.Status, error)
- func (b *Backend) Stop(ctx context.Context, r isolation.SlotRef) error
- type Bind
- type ConfigPaths
- type DeviceAllow
- type DeviceTarget
- type Ops
- type Rendered
- type Runner
- type TemplateData
Constants ¶
const MachinesDir = "/var/lib/machines"
MachinesDir is where systemd-nspawn machine root filesystems live.
Variables ¶
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).
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 ¶
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 Exec ¶
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 ¶
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 ¶
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 ¶
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 ¶
Registered reports registration only after successful inventory. Bootstrap callers must not treat a missing inspection tool as a clean host.
func RegisteredForCleanup ¶
RegisteredForCleanup answers Registered's question with the tolerance described on MissingTool. Cleanup uses this; admission must not.
func Remove ¶
Remove removes the machine rootfs using machinectl and falls back to force-removing the directory once machined no longer knows the machine.
func RemoveBPFFSMount ¶
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 ¶
Root returns the NodeRoot for the machine slot, rooted at its directory under MachinesDir.
func ServiceName ¶
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.
Types ¶
type Backend ¶
Backend is the systemd-nspawn isolation backend.
func (*Backend) Exec ¶
func (b *Backend) Exec(ctx context.Context, r isolation.SlotRef, cmd isolation.ExecSpec) (isolation.ExecResult, error)
Exec implements isolation.Backend.
func (*Backend) Logs ¶
func (*Backend) Logs(ctx context.Context, r isolation.SlotRef, opts isolation.LogOptions) (io.ReadCloser, error)
Logs 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.
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.
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.