Documentation
¶
Overview ¶
Package none is the isolation backend that runs the node stack directly on the host as agent-owned systemd units. Artifacts live in versioned directories under an agent-owned base, selected by a "current" symlink, so an upgrade is a symlink flip that can be flipped back.
The agent's node stack operations (cmd/agent/internal/noneops) build the Backend with its install and readiness hooks.
Index ¶
- Constants
- Variables
- func CheckWritable(paths ...string) error
- func DropInDir(dir, unit string) string
- func MkdirTraversable(dir string) error
- func RenderContainerd(s UnitSpec) []byte
- func RenderGPUSetupDropIn(cmds [][]string) []byte
- func RenderKubelet(s UnitSpec) []byte
- func RenderKubeletLaunchCheck(command string) []byte
- func RenderKubeletLaunchCheckDropIn() []byte
- func RenderLocalDNS(supervisor string) []byte
- func RenderLocalDNSDropIn() []byte
- func RenderPathDropIn(binDir string) []byte
- func WriteDropIns(dir string, s UnitSpec) ([]string, error)
- func WriteUnits(dir string, s UnitSpec) ([]string, error)
- type Backend
- func (b *Backend) Activate(r isolation.SlotRef) (string, error)
- func (b *Backend) Destroy(ctx context.Context, _ isolation.SlotRef) error
- func (b *Backend) Exec(ctx context.Context, _ isolation.SlotRef, cmd isolation.ExecSpec) (isolation.ExecResult, error)
- func (b *Backend) LinkCNIPlugins(version string) error
- func (b *Backend) Logs(ctx context.Context, _ isolation.SlotRef, opts isolation.LogOptions) (io.ReadCloser, error)
- func (*Backend) Mode() isolation.Mode
- func (b *Backend) Preflight() error
- func (b *Backend) PreflightOwnership() error
- func (b *Backend) PreflightStatic() error
- func (b *Backend) Prepare(ctx context.Context, n isolation.NodeSpec) (isolation.NodeRoot, error)
- func (b *Backend) PrepareVersion(ctx context.Context, n isolation.NodeSpec) (Root, error)
- func (b *Backend) Requirements() isolation.Requirements
- func (b *Backend) Restart(ctx context.Context, _ isolation.SlotRef) error
- func (b *Backend) Rollback(ctx context.Context, prev string) error
- func (b *Backend) Start(ctx context.Context, _ isolation.SlotRef) error
- func (b *Backend) Status(ctx context.Context, _ isolation.SlotRef) (isolation.Status, error)
- func (b *Backend) Stop(ctx context.Context, _ isolation.SlotRef) error
- func (b *Backend) UnlinkCNIPlugins() error
- func (b *Backend) Upgrade(ctx context.Context, r isolation.SlotRef, ...) error
- type HostFS
- type Layout
- func (l Layout) BinDir() string
- func (l Layout) CNIBinDir() string
- func (l Layout) Current() (string, error)
- func (l Layout) CurrentLink() string
- func (l Layout) Ensure(version string) (string, error)
- func (l Layout) Flip(version string) (string, error)
- func (l Layout) Remove(version string) error
- func (l Layout) RemoveAll() error
- func (l Layout) VersionDir(version string) string
- func (l Layout) Versions() ([]string, error)
- type Root
- type Runner
- type UnitSpec
Constants ¶
const ( UnitContainerd = "unbounded-containerd.service" UnitKubelet = "unbounded-kubelet.service" // UnitKubeletLaunchCheck is the allocation launch check the kubelet // requires on an allocation host; see RenderKubeletLaunchCheck. UnitKubeletLaunchCheck = "unbounded-kubelet-launch-check.service" )
Agent-owned unit names. They differ from the distro names so the agent never edits or removes a unit it did not create.
const ContainerdConfigPath = "/etc/unbounded/containerd/config.toml"
ContainerdConfigPath is the agent-owned containerd configuration the containerd unit runs with. It is not the distro's /etc/containerd/config.toml, so a host-native node never writes or deletes a file a host package owns.
const DefaultBase = "/opt/unbounded/node"
DefaultBase is the agent-owned directory that holds versioned artifacts.
const DefaultKubeletConfigPath = goalstates.KubeletConfigurationPath
DefaultKubeletConfigPath is where the node-start phases write the kubelet configuration, the same path the nspawn machine uses.
const HostCNIBinDir = "/opt/cni/bin"
HostCNIBinDir is the host directory containerd looks for CNI plugins in. It stays the CNI default because cluster add-ons (unbounded-net's IPAM plugin, for one) install their plugins there from a hostPath mount.
const SystemdDir = "/etc/systemd/system"
SystemdDir is where the agent writes its units.
const UnitLocalDNS = "unbounded-localdns.service"
UnitLocalDNS is the agent-owned CoreDNS unit. LocalDNS needs no machine to run in, so on this backend CoreDNS is a host unit. The network unit that creates the dummy interface and NOTRACK rules is shared with the nspawn mode and is not defined here.
const UnitLocalDNSNetwork = goalstates.LocalDNSNetworkUnit
UnitLocalDNSNetwork is the host network unit shared by every mode. It is written by the node-start phases, never by this backend.
Variables ¶
var DefaultHostFS = HostFS{ UnitDirs: []string{"/etc/systemd/system", "/run/systemd/system", "/usr/lib/systemd/system", "/lib/systemd/system"}, BinDirs: []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"}, Proc: "/proc", Cgroup: "/sys/fs/cgroup", }
DefaultHostFS is the real host.
var ErrVersionActive = errors.New("version is active")
ErrVersionActive is returned by Remove for the active version.
Functions ¶
func CheckWritable ¶
CheckWritable fails when any of paths cannot be written. A path that does not exist is checked through its nearest existing parent.
func MkdirTraversable ¶
MkdirTraversable creates dir with any missing parents and makes dir, and each parent it created, 0755 whatever the umask. The node's LocalDNS runs as a dynamic user, which has to traverse the base and version directories to reach its binaries; under the umask 027 of a hardened host, plain MkdirAll leaves them 0750 and the unit fails to execute. A parent that already existed keeps its mode: it is not the agent's to change.
func RenderContainerd ¶
RenderContainerd returns the containerd unit.
func RenderGPUSetupDropIn ¶
RenderGPUSetupDropIn returns the containerd drop-in that runs each command before containerd starts. A failure fails the start, as it does when the agent runs the same setup, and Restart= retries it.
func RenderKubelet ¶
RenderKubelet returns the kubelet unit. It orders after and wants the agent's containerd, never the distro's.
func RenderKubeletLaunchCheck ¶
RenderKubeletLaunchCheck returns the oneshot that runs the allocation launch check before every kubelet start on an allocation host. The kubelet requires it through RenderKubeletLaunchCheckDropIn. It is a separate unit, not an ExecStartPre, because RestartPreventExitStatus= applies only to a unit's main process: a final refusal must fail the kubelet's start rather than loop under its Restart=always. It has no [Install] section; only the kubelet pulls it in.
func RenderKubeletLaunchCheckDropIn ¶
func RenderKubeletLaunchCheckDropIn() []byte
RenderKubeletLaunchCheckDropIn returns the kubelet drop-in that makes every kubelet start wait for, and depend on, the launch check.
func RenderLocalDNS ¶
RenderLocalDNS returns the CoreDNS supervisor unit. supervisor is the path of the supervisor script in the version directory.
func RenderLocalDNSDropIn ¶
func RenderLocalDNSDropIn() []byte
RenderLocalDNSDropIn returns the drop-in that orders a node unit after LocalDNS and makes it depend on it, as the nspawn machine's units do.
func RenderPathDropIn ¶
RenderPathDropIn returns the drop-in that puts the version's bin directory first on PATH, so containerd finds containerd-shim-runc-v2 and runc and kubelet finds the tools it shells out to without touching the host's PATH.
func WriteDropIns ¶
WriteDropIns writes the PATH drop-in for every unit, the LocalDNS ordering drop-ins for the node units, and the LocalDNS unit itself when s.LocalDNS is set. It returns the paths written.
Types ¶
type Backend ¶
type Backend struct {
Log *slog.Logger
Layout Layout
// SystemdDir is where units are written. Empty means SystemdDir.
SystemdDir string
// Host is used by preflight. The zero value is DefaultHostFS.
Host *HostFS
// Run executes host commands. Nil uses executil.OutputCmd.
Run Runner
// Units is the template input written by Prepare.
Units UnitSpec
// Install places the version's artifacts and node configuration. Prepare
// calls it after the version directory exists and before the units are
// written. Nil installs nothing, which is what the conformance harness and
// unit tests want.
Install func(ctx context.Context, r isolation.NodeRoot) error
// AfterStart runs after the named unit has been started by Start, for
// readiness gates such as waiting for LocalDNS between CoreDNS and
// containerd. A failure stops Start.
AfterStart map[string]func(ctx context.Context) error
// AfterRestart is AfterStart for Restart.
AfterRestart map[string]func(ctx context.Context) error
// BeforeUpgradeStart runs during Upgrade after the old version has stopped
// and the link has flipped, before the new version starts. The agent uses
// it to clean up host networking as repave does and set it up for the new
// configuration.
BeforeUpgradeStart func(ctx context.Context) error
// BeforeRollbackStart runs during a failed Upgrade before the previous
// version starts again, so it starts on the configuration and host
// networking it was running with: after the link has flipped back on a
// rollback, and without a flip when the stop or the activation failed. A
// failure is reported with the rollback and does not stop it.
BeforeRollbackStart func(ctx context.Context) error
// RollbackUnits is the unit spec of the version a rollback returns to. Nil
// leaves the unit files as they are, which is right when they were never
// rewritten; the agent sets it from the configuration the node ran with.
RollbackUnits *UnitSpec
// BeforeStart runs at the start of Start and Restart, before any unit is
// touched. The agent uses it to prepare GPU support, which depends on the
// devices and driver present at that start. A failure stops the start.
BeforeStart func(ctx context.Context) error
// HostRoot prefixes the host paths grants name. Empty is the real host;
// tests point it at a directory tree.
HostRoot string
// GPUHostPaths are host paths that must exist when a grant asks for NVIDIA
// devices, because the stack cannot run GPU workloads without them. The
// agent sets the NVIDIA Container Toolkit's tools; empty checks nothing.
GPUHostPaths []string
// CheckPaths replaces Requirements().WritablePaths in Preflight when set.
// Tests and offline tooling use it to probe a temporary tree instead of the
// real /etc, /opt and /var.
CheckPaths []string
// LookPath resolves host commands for PreflightStatic. Nil skips the
// command check; the agent sets it to exec.LookPath.
LookPath func(file string) (string, error)
// Proc is the procfs mount Destroy scans for containerd shims that outlive
// the stopped units. Empty skips the scan, so tests and tools that build a
// Backend never signal processes by accident; the agent sets it to /proc.
Proc string
// CgroupRoot is the cgroup v2 mount Destroy kills the pods' cgroups under:
// ending a containerd shim leaves the pod's processes running. Empty skips
// it for the same reason as Proc; the agent sets it to /sys/fs/cgroup.
CgroupRoot string
// Kill signals one process for Destroy's reaping. Nil sends SIGKILL; tests
// set it so no real process is signaled.
Kill func(pid int) error
}
Backend runs the node stack directly on the host.
func NewDefault ¶
NewDefault returns a backend with the production defaults: the default layout and unit directory, host commands run for real, and Destroy reaping shims and pod cgroups under /proc and /sys/fs/cgroup. It is what a caller that only needs Status, Exec or Logs wants; the agent's node stack operations build a fully wired backend instead.
func (*Backend) Activate ¶
Activate points the current link at the root's version and returns the version it replaced.
func (*Backend) Destroy ¶
Destroy stops and disables the units, removes them with their drop-ins, kills container shims containerd leaves running, and removes the version directories (the whole base directory). Units that were never installed are skipped, so destroying a half-installed node completes instead of failing on systemctl errors for units that do not exist.
func (*Backend) Exec ¶
func (b *Backend) Exec(ctx context.Context, _ isolation.SlotRef, cmd isolation.ExecSpec) (isolation.ExecResult, error)
Exec runs the command on the host, which is the node stack's environment.
func (*Backend) LinkCNIPlugins ¶
LinkCNIPlugins links every CNI plugin of version into the host plugin directory, each to the plugin of the same name below the current link. A flip of the link therefore switches the plugins with the rest of the version, while plugins other components install in the host directory stay usable. A host file that is not a link, or a link that points outside the agent's base, belongs to someone else and is left alone. It is idempotent.
func (*Backend) Logs ¶
func (b *Backend) Logs(ctx context.Context, _ isolation.SlotRef, opts isolation.LogOptions) (io.ReadCloser, error)
Logs streams the journal for an agent-owned unit. With no unit it covers both.
func (*Backend) Preflight ¶
Preflight refuses to proceed when the host already has a kubelet or containerd the agent does not own, or a required path is read only. It mutates nothing. It is PreflightStatic followed by PreflightOwnership.
func (*Backend) PreflightOwnership ¶
PreflightOwnership refuses a host that already has a kubelet or containerd the agent does not own (unit, binary or running process). It fails on a host that is a running node, so a migration runs it only after the reset.
func (*Backend) PreflightStatic ¶
PreflightStatic checks what the host must provide whatever it runs now: the cgroup filesystem, the required host commands (when LookPath is set) and writable /etc, the node base and /var. None of it depends on the absence of a kubelet or containerd, so it is valid on a host that is a running node. A migration runs it before it resets or stops anything.
func (*Backend) Prepare ¶
Prepare installs the version directory and writes the agent-owned units. The artifacts themselves are placed in the returned layout's bin directory by the installers; the current link is not touched until Activate. It is for a node that is not running: a LocalDNS unit a previous configuration left is stopped before its file is removed.
func (*Backend) PrepareVersion ¶
PrepareVersion installs the version directory without touching the units, so a running node keeps running the units it started with. Upgrade writes the units once the old stack has stopped.
func (*Backend) Requirements ¶
func (b *Backend) Requirements() isolation.Requirements
Requirements implements isolation.Backend. /usr need not be writable.
func (*Backend) Rollback ¶
Rollback stops the stack and its pods, points the current link at prev, writes the units for RollbackUnits (when set), runs BeforeRollbackStart and starts prev. Upgrade calls it after a failure past the flip; the agent also calls it at startup to finish an upgrade a crash interrupted. Every step is attempted; the errors are joined.
func (*Backend) Start ¶
Start starts the stack in order, running the AfterStart gate of each unit before the next one starts.
func (*Backend) Stop ¶
Stop stops the stack in reverse order. Every unit is attempted so a failure on one does not leave the others running.
func (*Backend) UnlinkCNIPlugins ¶
UnlinkCNIPlugins removes the links LinkCNIPlugins made and nothing else, then the host plugin directory when that leaves it empty.
func (*Backend) Upgrade ¶
func (b *Backend) Upgrade(ctx context.Context, r isolation.SlotRef, waitReady func(context.Context) error) error
Upgrade switches to an installed version prepared with PrepareVersion: stop the stack, end its pods, flip the link, write the units for b.Units, start the stack, and wait for ready. If any step after the flip fails the stack is stopped and its pods ended again, the link is flipped back, the units are written for RollbackUnits, and the previous version is started again. The node is briefly down.
Pods are ended before BeforeUpgradeStart because that hook cleans the host networking the old pods use. A soft reboot (Restart) leaves them alone.
type HostFS ¶
type HostFS struct {
// UnitDirs are directories searched for systemd unit files.
UnitDirs []string
// BinDirs are directories searched for executables.
BinDirs []string
// Proc is the procfs mount.
Proc string
// Cgroup is the cgroup filesystem mount. Empty skips the cgroup check.
Cgroup string
}
HostFS locates the host paths preflight inspects. Tests point the fields at a temporary tree; production uses DefaultHostFS.
func (HostFS) DetectUnmanaged ¶
DetectUnmanaged returns one finding per kubelet or containerd the agent does not own: a unit file, an executable on the standard paths, or a running process. The agent refuses to start with any finding, since two kubelets or two containerds on one host fight over the same sockets and directories.
Binaries below the agent's own base are not findings, and a process whose executable lives below ownedBase is the agent's own.
type Layout ¶
type Layout struct {
// Base is the agent-owned directory. Empty means DefaultBase.
Base string
}
Layout is the on-disk arrangement of versioned artifacts.
func (Layout) BinDir ¶
BinDir is where the active version's executables are found. Units refer to binaries through the current link so a flip changes what they run.
func (Layout) CNIBinDir ¶
CNIBinDir is where the active version's CNI plugins are, through the current link.
func (Layout) CurrentLink ¶
CurrentLink is the symlink that selects the active version.
func (Layout) Ensure ¶
Ensure creates the version directory (and its bin directory) if needed, makes the base, version and bin directories 0755, and returns the version directory's path. It is idempotent.
func (Layout) Flip ¶
Flip atomically points the current link at an installed version and returns the version it replaced ("" if there was none). The new link is built beside the old one and renamed over it, so there is no moment without a link.
func (Layout) VersionDir ¶
VersionDir is the directory holding the artifacts of one version.
type Root ¶
type Root struct {
Layout Layout
Version string
// HostRoot prefixes the identity-mapped paths. Empty is the real host; tests
// and offline tooling point it at a directory tree.
HostRoot string
}
Root is the NodeRoot of one installed version. Every path is the identity mapping, which is the same-path contract for /etc/containerd, /var/lib/kubelet, /etc/cni and the rest, except the installer's bin, libexec and CNI plugin directories, which resolve into the version directory. That is what lets the installers the nspawn rootfs uses place binaries in <base>/<version>/bin unchanged.
type Runner ¶
type Runner func(ctx context.Context, log *slog.Logger, name string, args ...string) (string, error)
Runner runs a host command and returns its stdout. It is a seam for tests.
type UnitSpec ¶
type UnitSpec struct {
// BinDir is the directory the binaries are executed from, normally
// Layout.BinDir so the units follow the current link.
BinDir string
// KubeletConfigPath is the kubelet configuration file. Empty means
// DefaultKubeletConfigPath.
KubeletConfigPath string
// LocalDNS adds the CoreDNS unit and makes containerd and kubelet depend on
// it.
LocalDNS bool
// ContainerdConfigPath is the configuration containerd runs with. Empty
// means ContainerdConfigPath.
ContainerdConfigPath string
// GPUSetup are the commands containerd runs before each start to prepare
// GPU support: the NVIDIA CDI spec names driver libraries by version, so it
// is regenerated whenever the stack starts, a boot included, not only when
// the agent starts it. Empty, on a node without GPUs, writes no drop-in.
GPUSetup [][]string
// LaunchCheckCommand is the command the launch-check oneshot runs before
// every kubelet start, set only on an allocation host. It is a separate
// oneshot so a final refusal fails the kubelet's start rather than loops
// under the kubelet's Restart=always. Empty writes no launch check.
LaunchCheckCommand string
}
UnitSpec holds what the unit templates need.