Documentation
¶
Overview ¶
Package apply turns a parsed Config into actual host mutations. The System interface abstracts the OS primitives ; per-OS implementations live in apply_<os>.go behind build tags.
All System calls must be idempotent. A first-boot agent is allowed to crash and resume ; on resume, replaying the same Config against the same host must be a no-op rather than an error or a duplicate effect.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type System ¶
type System interface {
// Hostname returns the live hostname (post any previous SetHostname).
Hostname() (string, error)
// SetHostname commits the new name to durable config (/etc/hostname,
// /etc/myname, /etc/rc.conf depending on OS) AND applies it to the
// running kernel via the native command.
SetHostname(name string) error
// UserExists reports whether name appears in the passwd database.
UserExists(name string) (bool, error)
// CreateUser provisions a new user with the given primary shell ;
// the user's primary group is left to OS defaults (per-OS useradd
// behaviour). Groups in extraGroups are appended (created if
// missing). passwordHash, when non-empty, becomes the crypt(3)
// hash ; empty hash locks the password.
CreateUser(name, shell, passwordHash string, extraGroups []string) error
// AddUserToGroups appends extraGroups to an EXISTING user's
// supplementary groups. Used on the reconcile path so re-runs
// against an already-provisioned host catch up new group adds.
AddUserToGroups(name string, extraGroups []string) error
// AddAuthorizedKey appends key to user's ~/.ssh/authorized_keys
// (creating the directory + file with the right perms+owner).
// Re-adding an already-present key is a no-op.
AddAuthorizedKey(user, key string) error
// WriteFile lands content at path with the given mode + ownership.
// Existing files are diffed ; only on differing content/mode/owner
// is the file overwritten (atomic rename via tempfile).
WriteFile(path, content, mode, owner, group string) error
// PackageInstall installs every name in pkgs via the OS-native
// package manager (apt-get on debian-family, dnf on fedora-family,
// pkg_add on OpenBSD, pkg on FreeBSD, pkgin on NetBSD). Empty list
// is a no-op (the impl must NOT exec the package manager just to
// have it churn). Idempotent : re-installing already-present
// packages is the OS pkg manager's job (apt-get / pkg_add / pkg /
// pkgin all skip already-installed packages by default).
PackageInstall(pkgs []string) error
// RunShell executes cmd via the OS sh, returning stdout/stderr
// blended on a non-zero exit. The implementation logs the command
// + outcome at debug level.
RunShell(cmd string) error
}
System is the OS abstraction the apply layer talks to. Every method is idempotent : SetHostname on a host already named X is a no-op, CreateUser on an existing user only reconciles authorized_keys / shell / group membership, WriteFile compares content + mode + owner and only re-writes on diff.
func NewLinuxSystem ¶
NewLinuxSystem returns a System wired against the live Linux host.
func NewSystem ¶
NewSystem returns the System impl for the running OS. The actual implementations live behind build tags in apply_<os>.go ; each defines osNewSystem(*slog.Logger) System and this factory dispatches via runtime.GOOS as a defensive check (the build tag above already guarantees the right impl is linked in).
On unsupported platforms (darwin / windows / illumos / dragonflybsd / solaris), the binary doesn't link factory.go at all — see factory_unsupported.go which surfaces the error path. Tests on those platforms exercise memSystem directly.