Skip to content

Commit

Permalink
Merge pull request #114 from numtide/feat/godoc
Browse files Browse the repository at this point in the history
feat/godoc
  • Loading branch information
brianmcgee authored Sep 13, 2024
2 parents 6544c22 + 5dacfc2 commit dfa6075
Show file tree
Hide file tree
Showing 25 changed files with 995 additions and 820 deletions.
5 changes: 5 additions & 0 deletions docs/content/getting-started/generate-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ This will scan your system and produce a JSON-based report in a file named `fact
4. All the various bits of hardware that could be detected.
5. [System Management BIOS] information if available.

!!! tip

To use this report in your NixOS configuration, have a look at [NixOS Facter Modules].

[Nix]: https://nixos.org
[Numtide]: https://numtide.com
[Numtide Binary Cache]: https://numtide.cachix.org
[nixos-facter]: https://github.com/numtide/nixos-facter
[nixpkgs]: https://github.com/nixos/nixpkgs
[System Management BIOS]: https://wiki.osdev.org/System_Management_BIOS
[NixOS Facter Modules]: https://github.com/numtide/nixos-facter-modules
1 change: 0 additions & 1 deletion docs/content/js/.gitignore

This file was deleted.

1 change: 1 addition & 0 deletions docs/content/reference/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go_doc
80 changes: 79 additions & 1 deletion flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
hwinfo.inputs.nixpkgs.follows = "nixpkgs";
hwinfo.inputs.systems.follows = "systems";
hwinfo.inputs.blueprint.follows = "blueprint";

godoc.url = "github:numtide/godoc";
godoc.inputs.nixpkgs.follows = "nixpkgs";
godoc.inputs.systems.follows = "systems";
godoc.inputs.blueprint.follows = "blueprint";
godoc.inputs.treefmt-nix.follows = "treefmt-nix";
};

# Keep the magic invocations to minimum.
Expand Down
3 changes: 0 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package main

import (
Expand Down
5 changes: 0 additions & 5 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ validation:
docs_dir: docs/content

nav:
- Getting started:
- getting-started/generate-report.md
- Contributing:
- contributing/code.md
- contributing/docs.md

theme:
name: material
Expand Down
1 change: 1 addition & 0 deletions nix/devshells/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ perSystem.self.nixos-facter.overrideAttrs (old: {
pkgs.enumer
pkgs.delve
pkgs.pprof
pkgs.gotools
pkgs.golangci-lint
pkgs.cobra-cli
pkgs.fx # json tui
Expand Down
32 changes: 24 additions & 8 deletions nix/devshells/docs.nix
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
{pkgs, ...}:
{
pkgs,
perSystem,
...
}:
pkgs.mkShellNoCC {
packages = with pkgs;
[
mkdocs
]
++ (with pkgs.python3Packages; [
mike
mkdocs-material
]);
# Pop an empty shell on systems that aren't supported by godoc
lib.optionals (perSystem.godoc ? default)
([
perSystem.godoc.default
(pkgs.writeScriptBin "gen-reference" ''
out="./docs/content/reference/go_doc"
godoc -c -o $out .
'')
(pkgs.writeScriptBin "mkdocs" ''
# generate reference docs first
gen-reference
# execute the underlying command
${pkgs.mkdocs}/bin/mkdocs "$@"
'')
]
++ (with pkgs.python3Packages; [
mike
mkdocs-material
]));
}
2 changes: 1 addition & 1 deletion nix/packages/nixos-facter/tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
};
in
# for now we only run the tests in x86_64-linux since we don't have access to a bare-metal ARM box or a VM that supports nested
# virtualization which makes the test take forever and ultimately fail
# virtualisation which makes the test take forever and ultimately fail
pkgs.lib.optionalAttrs pkgs.stdenv.isx86_64 {
basic = diskoLib.testLib.makeDiskoTest {
inherit pkgs;
Expand Down
12 changes: 8 additions & 4 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Package build contains constants and values set at build time via -X flags.
package build

var (
Name = "nixos-facter"
// Name is the program name, typically set via Nix to match the derivation's `pname`.
Name = "nixos-facter"
// Version is the program version, typically set via Nix to match the derivation's `version`.
Version = "v0.0.0+dev"
System = ""

// ReportVersion is used to indicate significant changes in the report output
// System is the architecture that this program was built for e.g. x86_64-linux.
// It is set via Nix to match the Nixpkgs system.
System = ""
// ReportVersion is used to indicate significant changes in the report output and is embedded JSON report produced.
ReportVersion uint = 2
)
7 changes: 7 additions & 0 deletions pkg/ephem/ephem.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Package ephem contains utilities for capturing ephemeral aspects of a target machine.
//
// Currently, it only supports capturing swap configurations.
// Eventually it will capture more things, such as filesystems.
package ephem

import (
Expand All @@ -15,6 +19,9 @@ var deviceGlobs = []string{
"/dev/disk/by-label/*",
}

// StableDevicePath takes a device path and converts it into a more stable form.
// For example, /dev/nvme* is assigned on startup by detection order which is not consistent.
// A disk path of the form /dev/disk/by-uuid/* is not startup-dependent.
func StableDevicePath(device string) (string, error) {
l := log.WithPrefix("stableDevicePath")

Expand Down
21 changes: 16 additions & 5 deletions pkg/ephem/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ var (
swapEntryRegex = regexp.MustCompile(`^(.*?)\s+(partition|file)\s+(.*?)\s+(.*?)\s+(.*?)$`)
)

// SwapType represents the type of swap space, either file or partition.
//
//go:generate enumer -type=SwapType -json -transform=snake -trimprefix SwapType -output=./swap_enum_type.go
type SwapType uint

Expand All @@ -22,14 +24,22 @@ const (
SwapTypePartition
)

// SwapEntry represents a swap entry.
type SwapEntry struct {
Filename string `json:"path"`
Type SwapType `json:"type"`
Size uint64 `json:"size"`
Used uint64 `json:"used"`
Priority int32 `json:"priority"`
// Type is the type of swap e.g. partition or file.
Type SwapType `json:"type"`
// Filename is the path to the swap device or file.
Filename string `json:"path"`
// Size is the total size of the swap in kilobytes.
Size uint64 `json:"size"`
// Used is the amount of swap space currently in use, in kilobytes.
Used uint64 `json:"used"`
// Priority determines the order in which swap spaces are used.
// Higher numbers have higher priority.
Priority int32 `json:"priority"`
}

// SwapEntries retrieves the list of swap entries from the system and resolves stable device paths for each entry.
func SwapEntries() ([]*SwapEntry, error) {
f, err := os.Open("/proc/swaps")
if err != nil {
Expand All @@ -53,6 +63,7 @@ func SwapEntries() ([]*SwapEntry, error) {
return devices, nil
}

// ReadSwapFile reads swap entries from an io.Reader, validating the format and parsing each entry.
func ReadSwapFile(reader io.Reader) ([]*SwapEntry, error) {
scanner := bufio.NewScanner(reader)
if !scanner.Scan() {
Expand Down
106 changes: 106 additions & 0 deletions pkg/facter/facter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Package facter contains types and utilities for scanning a system and generating a report, detailing key aspects of
// the system and its connected hardware.
package facter

import (
"fmt"

"github.com/numtide/nixos-facter/pkg/ephem"

"github.com/numtide/nixos-facter/pkg/build"
"github.com/numtide/nixos-facter/pkg/hwinfo"
"github.com/numtide/nixos-facter/pkg/virt"
)

// Report represents a detailed report on the system’s hardware, virtualisation, SMBios, and swap entries.
type Report struct {
// Version is a monotonically increasing number,
// used to indicate breaking changes or new features in the report output.
Version uint `json:"version"`

// System indicates the system architecture e.g. x86_64-linux.
System string `json:"system"`

// Virtualisation indicates the type of virtualisation or container environment present on the system.
Virtualisation virt.Type `json:"virtualisation"`

// Hardware provides detailed information about the system’s hardware components, such as CPU, memory, and peripherals.
Hardware Hardware `json:"hardware,omitempty"`

// Smbios provides detailed information about the system's SMBios data, such as BIOS, board, chassis, memory, and processors.
Smbios Smbios `json:"smbios,omitempty"`

// Swap contains a list of swap entries representing the system's swap devices or files and their respective details.
Swap []*ephem.SwapEntry `json:"swap,omitempty"`
}

// Scanner defines a type responsible for scanning and reporting system hardware information.
type Scanner struct {
// Swap indicates whether the system swap information should be reported.
Swap bool

// Ephemeral indicates whether the scanner should report ephemeral details,
// such as swap.
Ephemeral bool

// Features is a list of ProbeFeature types that should be scanned for.
Features []hwinfo.ProbeFeature
}

// Scan scans the system's hardware and software information and returns a report.
// It also detects IOMMU groups and handles errors gracefully if scanning fails.
func (s *Scanner) Scan() (*Report, error) {
var err error
report := Report{
Version: build.ReportVersion,
}

if build.System == "" {
return nil, fmt.Errorf("system is not set")
}
report.System = build.System

var smbios []hwinfo.Smbios
var devices []hwinfo.HardwareDevice

smbios, devices, err = hwinfo.Scan(s.Features)
if err != nil {
return nil, fmt.Errorf("failed to scan hardware: %w", err)
}

// read iommu groups
iommuGroups, err := hwinfo.IOMMUGroups()
if err != nil {
return nil, fmt.Errorf("failed to read iommu groups: %w", err)
}

for idx := range devices {
// lookup iommu group before adding to the report
device := devices[idx]
groupId, ok := iommuGroups[device.SysfsId]
if ok {
device.SysfsIOMMUGroupId = &groupId
}
if err = report.Hardware.add(device); err != nil {
return nil, fmt.Errorf("failed to add to hardware report: %w", err)
}
}

for idx := range smbios {
if err = report.Smbios.add(smbios[idx]); err != nil {
return nil, fmt.Errorf("failed to add to smbios report: %w", err)
}
}

if report.Virtualisation, err = virt.Detect(); err != nil {
return nil, fmt.Errorf("failed to detect virtualisation: %w", err)
}

if s.Ephemeral || s.Swap {
if report.Swap, err = ephem.SwapEntries(); err != nil {
return nil, fmt.Errorf("failed to detect swap devices: %w", err)
}
}

return &report, nil
}
Loading

0 comments on commit dfa6075

Please sign in to comment.