Files
node-lookup/internal/puppet/config.go
T
unkinben 8800d5ce35
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add pburl and pblastreport companion tools to the RPM
node-lookup output is useful for pivoting to Puppetboard, but there was no
quick way to turn a list of hosts into Puppetboard node-page URLs or to see
when each host last ran Puppet. These two tools close that gap and ship in the
same RPM so they are available wherever node-lookup is.

- Add pburl: reads hostnames from args or piped node-lookup output and prints
  "<host> <puppetboard-node-page-url>".
- Add pblastreport: prints "<host>\t<last-report-time>\t<url>" using
  report_timestamp from the PuppetDB v4 nodes endpoint. Supports --relative/-r
  (relative age) and --timezone/-z <IANA> (default: local timezone).
- Add internal/puppet package shared by both tools: config load, PuppetDB
  nodes query, Puppetboard URL construction, and no-TTY-safe stdin host reading.
- Add puppetboard_url config key (env NODE_LOOKUP_PUPPETBOARD_URL) to the
  shared config so config init/show scaffold it for the whole tool family.
- Build all three binaries individually (each is its own main package) and
  generate per-binary bash/zsh/fish completions in the Makefile, build-rpm.sh,
  and nfpm spec; cross-compile and attach all three per os/arch in the release
  pipeline.
- Document the tools, config key, and env var in AGENTS.md.
2026-07-16 22:31:21 +10:00

91 lines
2.6 KiB
Go

// Package puppet holds the small pieces of PuppetDB/Puppetboard plumbing shared
// by the node-lookup companion tools (pburl, pblastreport): config loading,
// PuppetDB "nodes" queries, Puppetboard URL construction, and reading hostnames
// from piped node-lookup output.
//
// It intentionally reads the SAME config file, env vars, and defaults as the
// node-lookup CLI so a single `~/.config/node-lookup/config.yaml` configures
// every tool in the family.
package puppet
import (
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
const (
// DefaultPuppetDBURL is the PuppetDB v4 facts endpoint (shared with node-lookup).
DefaultPuppetDBURL = "http://puppetdbapi.service.consul:8080/pdb/query/v4/facts"
// DefaultRoleFact is the role fact node-lookup queries with -R.
DefaultRoleFact = "enc_role"
// DefaultPuppetboardURL is the base URL of the Puppetboard web UI.
DefaultPuppetboardURL = "https://puppetboard.k8s.syd1.au.unkin.net"
appName = "node-lookup"
configFileName = "config.yaml"
)
// Config mirrors node-lookup's config plus the puppetboard_url key used by the
// companion tools. Fields map 1:1 to config file keys and env vars.
type Config struct {
PuppetDBURL string `yaml:"puppetdb_url"`
RoleFact string `yaml:"role_fact"`
PuppetboardURL string `yaml:"puppetboard_url"`
}
// DefaultConfig returns the built-in defaults.
func DefaultConfig() Config {
return Config{
PuppetDBURL: DefaultPuppetDBURL,
RoleFact: DefaultRoleFact,
PuppetboardURL: DefaultPuppetboardURL,
}
}
// ConfigDir returns the XDG_CONFIG_HOME/node-lookup directory.
func ConfigDir() string {
base := os.Getenv("XDG_CONFIG_HOME")
if base == "" {
home, _ := os.UserHomeDir()
base = filepath.Join(home, ".config")
}
return filepath.Join(base, appName)
}
// ConfigPath returns the full path to the shared config file.
func ConfigPath() string {
return filepath.Join(ConfigDir(), configFileName)
}
// Load reads the config file (if present), then applies env var overrides.
// Precedence (lowest → highest): defaults < config file < env vars.
func Load() (Config, error) {
cfg := DefaultConfig()
path := ConfigPath()
data, err := os.ReadFile(path)
if err != nil && !os.IsNotExist(err) {
return cfg, fmt.Errorf("reading config %s: %w", path, err)
}
if err == nil {
if err := yaml.Unmarshal(data, &cfg); err != nil {
return cfg, fmt.Errorf("parsing config %s: %w", path, err)
}
}
if v := os.Getenv("NODE_LOOKUP_URL"); v != "" {
cfg.PuppetDBURL = v
}
if v := os.Getenv("NODE_LOOKUP_ROLE_FACT"); v != "" {
cfg.RoleFact = v
}
if v := os.Getenv("NODE_LOOKUP_PUPPETBOARD_URL"); v != "" {
cfg.PuppetboardURL = v
}
return cfg, nil
}