Documentation
¶
Overview ¶
Package hostfacts collects normalized Linux host state, reports per-fact availability, and tracks it as protocol ops.
Index ¶
- Constants
- Variables
- func IsKubernetesNode(procRoot, fsRoot string) (bool, string, error)
- func ParseOSRelease(b []byte) map[string]string
- func RenderCatalog() string
- func ScopeKey(id string) string
- type Collector
- type Entry
- type HostTracker
- type NetInterface
- type Options
- type Package
- type Result
- type Snapshot
- type Source
- type Status
- type Systemd
- type SystemdUnit
- type Uname
Constants ¶
const ( FactOS = "fact/os" FactHostname = "fact/hostname" FactMachineID = "fact/machine_id" FactKernel = "fact/kernel" FactCPU = "fact/cpu" FactMemory = "fact/memory" FactBlockDevices = "fact/block_devices" FactFilesystems = "fact/filesystems" FactMounts = "fact/mounts" FactInterfaces = "fact/interfaces" FactSockets = "fact/sockets" FactUnits = "fact/units" FactTimers = "fact/timers" FactPackages = "fact/packages" FactProcesses = "fact/processes" EdgeIDUnitProcess = "edge/unit_process" EdgeIDMountDevice = "edge/mount_device" EdgeIDMountFilesystem = "edge/mount_filesystem" EdgeIDPartitionDisk = "edge/partition_disk" EdgeIDPackageUnit = "edge/package_unit" EdgeIDSocketProcess = "edge/socket_process" EdgeIDSocketUnit = "edge/socket_unit" EdgeIDTimerUnit = "edge/timer_unit" )
Catalog IDs of facts and edges.
const ( KindOS = "host/OS" KindKernel = "host/Kernel" KindCPU = "host/CPU" KindMemory = "host/Memory" KindBlockDevice = "host/BlockDevice" KindFilesystem = "host/Filesystem" KindMount = "host/Mount" KindInterface = "host/Interface" KindSocket = "host/Socket" KindUnit = "host/Unit" KindTimer = "host/Timer" KindPackage = "host/Package" KindProcess = "host/Process" )
Resource kinds.
const ( EdgeMainProcess = "main_process" EdgeDevice = "device" EdgeFilesystem = "filesystem" EdgePartitionOf = "partition_of" EdgeProvidesUnit = "provides_unit" EdgeProcess = "process" EdgeUnit = "unit" EdgeTriggers = "triggers" )
Edge types.
const ( ReasonSourceAbsent = "source_absent" ReasonPermission = "permission_denied" ReasonDBus = "dbus_unavailable" ReasonReadFailed = "read_failed" ReasonOtherUserFD = "other_user_fd" ReasonOwnerNotFound = "owner_not_found" ReasonDependency = "dependency_unavailable" ReasonProcessVanished = "process_vanished" ReasonCapacityDenied = "capacity_permission_denied" )
Reason codes carried in scope statuses and socket fields.
Variables ¶
var ( DefaultMountPointsExclude = regexp.MustCompile(`^/(dev|proc|sys|run/credentials/.+|run/user/.+|run/netns/.+|run/docker/.+|run/containerd/.+|run/snapd/ns(/.*)?|var/lib/docker/.+|var/lib/containers/storage/.+|var/lib/kubelet/pods/.+)($|/)`) DefaultFSTypesExclude = regexp.MustCompile(`^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|sysfs|tracefs|efivarfs)$`) DefaultInterfacesExclude = regexp.MustCompile(`^veth`) DefaultUnitTypes = []string{"service", "socket", "target", "timer", "mount", "automount", "swap", "path", "slice"} )
Default exclusions follow node_exporter's filesystem collector plus per-session and container paths.
var Catalog = []Entry{ {ID: FactOS, Kind: KindOS, Fields: []string{"id", "id_like", "name", "pretty_name", "version_id", "version_codename", "variant_id"}, Source: "`/etc/os-release`, falling back to `/usr/lib/os-release`", Permission: "world-readable file", NonRoot: "available"}, {ID: FactHostname, Kind: KindOS, Fields: []string{"hostname"}, Source: "`uname(2)` nodename", Permission: "none", NonRoot: "available"}, {ID: FactMachineID, Kind: KindOS, Fields: []string{"machine_id"}, Source: "`/etc/machine-id`", Permission: "world-readable file", NonRoot: "available"}, {ID: FactKernel, Kind: KindKernel, Whole: true, Fields: []string{"sysname", "release", "version", "machine"}, Source: "`uname(2)`", Permission: "none", NonRoot: "available"}, {ID: FactCPU, Kind: KindCPU, Whole: true, Fields: []string{"model_name", "vendor_id", "logical_cpus", "cores", "packages"}, Source: "`/proc/cpuinfo`", Permission: "world-readable procfs file", NonRoot: "available"}, {ID: FactMemory, Kind: KindMemory, Whole: true, Fields: []string{"mem_total_bytes", "swap_total_bytes"}, Source: "`/proc/meminfo`", Permission: "world-readable procfs file", NonRoot: "available"}, {ID: FactBlockDevices, Kind: KindBlockDevice, Whole: true, Fields: []string{"device", "size_bytes", "rotational", "removable", "read_only", "model", "partition", "dm_name"}, Source: "`/sys/block/<dev>`: `dev`, `size`, `queue/rotational`, `removable`, `ro`, `device/model`, `dm/name`, partition subdirectories", Permission: "world-readable sysfs", NonRoot: "available"}, {ID: FactFilesystems, Kind: KindFilesystem, Whole: true, Fields: []string{"fstype", "source", "device", "size_bytes", "used_pct_bucket", "inodes_total", "capacity_unavailable"}, Source: "`/proc/self/mountinfo` grouped by superblock, capacity from `statfs(2)` on the first mount point; used space is bucketed to 10 percent", Permission: "search permission on the mount point path", NonRoot: "available; a mount point below a directory the agent cannot search reports `capacity_unavailable`"}, {ID: FactMounts, Kind: KindMount, Whole: true, Fields: []string{"mount_point", "source", "fstype", "root", "device", "read_only", "options"}, Source: "`/proc/self/mountinfo`, excluding pseudo filesystems and per-session or container mount points", Permission: "world-readable procfs file", NonRoot: "available"}, {ID: FactInterfaces, Kind: KindInterface, Whole: true, Fields: []string{"index", "mtu", "hardware_addr", "flags", "addresses", "operstate"}, Source: "netlink link and address dumps (`net.Interfaces`), `/proc/net/if_inet6` to drop temporary IPv6 addresses, `/sys/class/net/<if>/operstate`", Permission: "none", NonRoot: "available"}, {ID: FactSockets, Kind: KindSocket, Whole: true, Fields: []string{"protocol", "address", "port", "uid", "uids", "inodes", "unavailable_edges", "unavailable_reason"}, Source: "`/proc/net/tcp`, `tcp6` (state LISTEN), `udp`, `udp6` (unconnected bound sockets) of the agent's network namespace", Permission: "world-readable procfs files", NonRoot: "available"}, {ID: FactUnits, Kind: KindUnit, Whole: true, Fields: []string{"description", "load_state", "active_state", "sub_state", "unit_file_state", "fragment_path"}, Source: "D-Bus `org.freedesktop.systemd1.Manager.ListUnits`; `Unit` properties `FragmentPath`, `UnitFileState`; `Service` property `MainPID`", Permission: "system bus connection; read-only property queries are allowed to unprivileged clients by the systemd bus policy", NonRoot: "available where systemd runs"}, {ID: FactTimers, Kind: KindTimer, Whole: true, Fields: []string{"description", "load_state", "active_state", "sub_state", "unit_file_state", "fragment_path", "scheduled", "last_trigger_day"}, Source: "D-Bus `Timer` properties `Unit`, `NextElapseUSecRealtime` (reported only as `scheduled`), `LastTriggerUSec` (bucketed to the UTC day)", Permission: "system bus connection", NonRoot: "available where systemd runs"}, {ID: FactPackages, Kind: KindPackage, Whole: true, Fields: []string{"manager", "name", "version", "arch", "state"}, Source: "dpkg `/var/lib/dpkg/status`; rpm database (`rpmdb.sqlite`, `Packages.db`, or Berkeley DB `Packages`) via go-rpmdb; apk `/lib/apk/db/installed`", Permission: "world-readable package databases", NonRoot: "available"}, {ID: FactProcesses, Kind: KindProcess, Whole: true, Fields: []string{"pid", "comm", "uid", "cmdline"}, Source: "`/proc/<pid>/stat`, `/proc/<pid>/status`; `cmdline` only for allowlisted comm names, redacted. Only unit main processes and socket owners are collected", Permission: "world-readable procfs files (hidden when procfs is mounted with `hidepid`)", NonRoot: "available unless `hidepid` is set"}, {ID: EdgeIDUnitProcess, Kind: KindUnit, EdgeType: EdgeMainProcess, ToKinds: []string{KindProcess}, DependsOn: []string{FactUnits}, Source: "`Service.MainPID` resolved to `/proc/<pid>/stat`", Permission: "as host/Process", NonRoot: "available"}, {ID: EdgeIDMountDevice, Kind: KindMount, EdgeType: EdgeDevice, ToKinds: []string{KindBlockDevice}, DependsOn: []string{FactMounts, FactBlockDevices}, Source: "mountinfo `major:minor`, else the mount source path, matched to `/sys/block` devices and device-mapper names", Permission: "none", NonRoot: "available"}, {ID: EdgeIDMountFilesystem, Kind: KindMount, EdgeType: EdgeFilesystem, ToKinds: []string{KindFilesystem}, DependsOn: []string{FactMounts, FactFilesystems}, Source: "mountinfo superblock `major:minor`", Permission: "none", NonRoot: "available"}, {ID: EdgeIDPartitionDisk, Kind: KindBlockDevice, EdgeType: EdgePartitionOf, ToKinds: []string{KindBlockDevice}, DependsOn: []string{FactBlockDevices}, Source: "partition subdirectories of `/sys/block/<disk>`", Permission: "none", NonRoot: "available"}, {ID: EdgeIDPackageUnit, Kind: KindPackage, EdgeType: EdgeProvidesUnit, ToKinds: []string{KindUnit, KindTimer}, DependsOn: []string{FactPackages, FactUnits, FactTimers}, Source: "unit `FragmentPath` matched to dpkg `/var/lib/dpkg/info/*.list` or rpm file lists (usr-merge aliases included)", Permission: "world-readable package databases", NonRoot: "available"}, {ID: EdgeIDSocketProcess, Kind: KindSocket, EdgeType: EdgeProcess, ToKinds: []string{KindProcess}, DependsOn: []string{FactSockets}, Source: "`socket:[inode]` links in `/proc/<pid>/fd`, read only for processes of the agent's own UID", Permission: "ptrace read access to the owning process, which a non-root user has only for its own processes", NonRoot: "partial: sockets owned by other users report `unavailable_edges` with reason `other_user_fd`"}, {ID: EdgeIDSocketUnit, Kind: KindSocket, EdgeType: EdgeUnit, ToKinds: []string{KindUnit}, DependsOn: []string{FactSockets, FactUnits}, Source: "`/proc/<pid>/cgroup` of the owning process matched to a loaded unit", Permission: "requires the socket to process edge", NonRoot: "partial, as the socket to process edge"}, {ID: EdgeIDTimerUnit, Kind: KindTimer, EdgeType: EdgeTriggers, ToKinds: []string{KindUnit}, DependsOn: []string{FactTimers, FactUnits}, Source: "`Timer.Unit` property", Permission: "system bus connection", NonRoot: "available where systemd runs"}, }
Catalog lists every host fact and edge the collectors produce.
var DefaultRPMPaths = []string{
"/var/lib/rpm/rpmdb.sqlite", "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"/var/lib/rpm/Packages.db", "/usr/lib/sysimage/rpm/Packages.db",
"/var/lib/rpm/Packages", "/usr/lib/sysimage/rpm/Packages",
}
DefaultRPMPaths are probed in order; the first existing database is read.
var ErrNoSuchUnit = errors.New("hostfacts: no such unit")
ErrNoSuchUnit reports a unit that disappeared between listing and querying.
var Reasons = [][2]string{ {ReasonSourceAbsent, "The documented source does not exist on this host (for example no systemd, or no package database)."}, {ReasonPermission, "The agent user cannot read the source (restrictive modes, or procfs mounted with `hidepid`)."}, {ReasonDBus, "The system bus or `org.freedesktop.systemd1` is not reachable."}, {ReasonReadFailed, "The source exists but reading or parsing it failed."}, {ReasonOtherUserFD, "Mapping the socket to its process needs `/proc/<pid>/fd` of another user, which a non-root user cannot read."}, {ReasonOwnerNotFound, "The socket belongs to the agent's UID but no process of that UID holds it."}, {ReasonDependency, "The edge depends on a fact that is unavailable in this collection."}, {ReasonProcessVanished, "The process exited between listing and reading."}, {ReasonCapacityDenied, "`statfs(2)` on the mount point was denied; the filesystem is reported without capacity."}, }
Reasons documents every reason code.
Functions ¶
func IsKubernetesNode ¶
IsKubernetesNode reports whether this host runs a kubelet or holds kubelet state, with the evidence; host mode then refuses to start.
func ParseOSRelease ¶
ParseOSRelease parses os-release(5) content.
Types ¶
type Collector ¶
type Collector struct {
// contains filtered or unexported fields
}
Collector gathers host facts from the configured sources.
func NewCollector ¶
NewCollector returns a collector over o.
type Entry ¶
type Entry struct {
ID string
Kind string
EdgeType string
ToKinds []string
Fields []string
Whole bool
Source string
Permission string
NonRoot string
DependsOn []string
}
Entry catalogs one fact or edge with its source and the permission it needs (H4a).
type HostTracker ¶
type HostTracker struct {
// contains filtered or unexported fields
}
HostTracker turns successive snapshots into canonical ops against the last emitted state.
func NewHostTracker ¶
NewHostTracker diffs against base (an empty state when nil), for example the state recovered from the spool.
func (*HostTracker) Collect ¶
func (t *HostTracker) Collect(ctx context.Context) (Result, error)
Collect gathers a snapshot, carries forward what failed to collect, and returns the ops from the previous state.
func (*HostTracker) State ¶
func (t *HostTracker) State() *protocol.State
State returns a copy of the last emitted state.
type NetInterface ¶
type NetInterface struct {
Name string
Index int
MTU int
HardwareAddr string
Flags []string
Addrs []string
}
NetInterface is one network interface with its addresses in CIDR form.
func SystemInterfaces ¶
func SystemInterfaces() ([]NetInterface, error)
SystemInterfaces lists interfaces and addresses through the standard library (netlink on Linux).
type Options ¶
type Options struct {
ProcRoot string
SysRoot string
EtcRoot string
UsrLibRoot string
DpkgDir string
RPMPaths []string
APKInstalled string
// Systemd is used when set; otherwise DialSystemd is called on each collection until it succeeds.
Systemd Systemd
DialSystemd func(ctx context.Context) (Systemd, error)
Uname func() (Uname, error)
Interfaces func() ([]NetInterface, error)
Statfs func(path string, st *unix.Statfs_t) error
UID int
CmdlineAllowlist []string
Redactor *redact.Redactor
MountPointsExclude *regexp.Regexp
FSTypesExclude *regexp.Regexp
InterfacesExclude *regexp.Regexp
UnitTypes []string
}
Options configures the collectors. Zero values are replaced by DefaultOptions values.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns production paths and system sources.
type Package ¶
Package is one installed package with the systemd unit files it owns.
func ParseAPKInstalled ¶
ParseAPKInstalled parses the apk installed database.
func ParseDpkgStatus ¶
ParseDpkgStatus parses /var/lib/dpkg/status, returning packages that are not removed.
type Result ¶
type Result struct {
State *protocol.State
Ops []protocol.Op
Scopes map[string]protocol.ScopeStatus
}
Result is one tracked collection.
type Snapshot ¶
type Snapshot struct {
Resources map[string]protocol.Resource
Edges map[protocol.EdgeKey]map[string]any
Status map[string]Status
}
Snapshot is the result of one collection before diffing.
type Status ¶
type Status struct {
State protocol.ScopeState
Reason string
}
Status is the availability of one cataloged fact or edge in one collection.
type Systemd ¶
type Systemd interface {
ListUnits(ctx context.Context) ([]SystemdUnit, error)
// Properties returns the named properties of iface on the object at path.
Properties(ctx context.Context, path, iface string, names ...string) (map[string]any, error)
Close() error
}
Systemd is the read-only systemd manager surface the collectors use.
func DialSystemBus ¶
DialSystemBus connects to the system bus.
func NewDBusSystemd ¶
NewDBusSystemd wraps an authenticated bus connection.
type SystemdUnit ¶
SystemdUnit is one entry of org.freedesktop.systemd1.Manager.ListUnits.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package journal is a pure-Go, read-only reader of the systemd journal file format.
|
Package journal is a pure-Go, read-only reader of the systemd journal file format. |
|
Package nodemetrics runs upstream node_exporter collectors in process so node_* series and upstream rules work unchanged.
|
Package nodemetrics runs upstream node_exporter collectors in process so node_* series and upstream rules work unchanged. |
|
Package sqlitedb reads rowid tables of SQLite files (with committed WAL frames) in pure Go and serves them to go-rpmdb as a database/sql driver.
|
Package sqlitedb reads rowid tables of SQLite files (with committed WAL frames) in pure Go and serves them to go-rpmdb as a database/sql driver. |