Add pburl and pblastreport companion tools to the RPM
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

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.
This commit is contained in:
2026-07-16 22:31:21 +10:00
parent e070357d3f
commit 8800d5ce35
18 changed files with 881 additions and 46 deletions
+20 -10
View File
@@ -19,10 +19,11 @@ import (
)
const (
defaultPuppetDBURL = "http://puppetdbapi.service.consul:8080/pdb/query/v4/facts"
defaultRoleFact = "enc_role"
configFileName = "config.yaml"
appName = "node-lookup"
defaultPuppetDBURL = "http://puppetdbapi.service.consul:8080/pdb/query/v4/facts"
defaultRoleFact = "enc_role"
defaultPuppetboardURL = "https://puppetboard.k8s.syd1.au.unkin.net"
configFileName = "config.yaml"
appName = "node-lookup"
)
var version = "dev"
@@ -32,12 +33,17 @@ var version = "dev"
type config struct {
PuppetDBURL string `yaml:"puppetdb_url"`
RoleFact string `yaml:"role_fact"`
// PuppetboardURL is not used by node-lookup itself; it is scaffolded here so
// the shared config file also configures the companion tools (pburl,
// pblastreport) that read this same file.
PuppetboardURL string `yaml:"puppetboard_url"`
}
func defaultConfig() config {
return config{
PuppetDBURL: defaultPuppetDBURL,
RoleFact: defaultRoleFact,
PuppetDBURL: defaultPuppetDBURL,
RoleFact: defaultRoleFact,
PuppetboardURL: defaultPuppetboardURL,
}
}
@@ -78,6 +84,9 @@ func loadConfig() (config, error) {
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
}
@@ -96,7 +105,7 @@ func writeDefaultConfig() error {
cfg := defaultConfig()
data, _ := yaml.Marshal(cfg)
header := []byte("# node-lookup configuration\n# Fields can be overridden with env vars: NODE_LOOKUP_URL, NODE_LOOKUP_ROLE_FACT\n\n")
header := []byte("# node-lookup configuration\n# Fields can be overridden with env vars: NODE_LOOKUP_URL, NODE_LOOKUP_ROLE_FACT, NODE_LOOKUP_PUPPETBOARD_URL\n# puppetboard_url is used by the companion tools (pburl, pblastreport).\n\n")
if err := os.WriteFile(path, append(header, data...), 0o644); err != nil {
return fmt.Errorf("writing config: %w", err)
}
@@ -436,9 +445,10 @@ func main() {
Use: "show",
Short: "Print the active configuration",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("config file : %s\n", configPath())
fmt.Printf("puppetdb_url: %s\n", cfg.PuppetDBURL)
fmt.Printf("role_fact : %s\n", cfg.RoleFact)
fmt.Printf("config file : %s\n", configPath())
fmt.Printf("puppetdb_url : %s\n", cfg.PuppetDBURL)
fmt.Printf("role_fact : %s\n", cfg.RoleFact)
fmt.Printf("puppetboard_url: %s\n", cfg.PuppetboardURL)
return nil
},
SilenceUsage: true,