Files
node-lookup/cmd/pblastreport/main.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

98 lines
2.9 KiB
Go

// Command pblastreport shows each host's last Puppet report time alongside its
// Puppetboard node-page URL.
//
// It reads hostnames from its arguments or from piped node-lookup output,
// queries PuppetDB for each node's report_timestamp, and prints a
// tab-separated "<host> <last-report> <puppetboard-url>" line:
//
// node-lookup -R | pblastreport
// pblastreport --relative host1.example.net
// pblastreport --timezone Asia/Singapore host1.example.net
package main
import (
"fmt"
"os"
"time"
"node-lookup/internal/puppet"
"github.com/spf13/cobra"
)
var version = "dev"
func main() {
cfg, err := puppet.Load()
if err != nil {
fmt.Fprintln(os.Stderr, "config error:", err)
os.Exit(1)
}
var (
relative bool
tz string
boardURL string
pdbURL string
)
root := &cobra.Command{
Use: "pblastreport [host...]",
Short: "Show each host's last Puppet report time and Puppetboard URL.",
Long: "Reads hostnames from arguments or piped node-lookup output and prints, per\n" +
"host, the time of its last Puppet report and its Puppetboard node-page URL.\n" +
"Times are shown in the local timezone unless --timezone is given, or as a\n" +
"relative age with --relative. Example: node-lookup -R | pblastreport -r",
Args: cobra.ArbitraryArgs,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("puppetboard-url") {
cfg.PuppetboardURL = boardURL
}
if cmd.Flags().Changed("url") {
cfg.PuppetDBURL = pdbURL
}
loc := time.Local
if tz != "" {
l, err := time.LoadLocation(tz)
if err != nil {
return fmt.Errorf("invalid timezone %q: %w", tz, err)
}
loc = l
}
hosts := puppet.ReadHosts(os.Stdin, args)
if len(hosts) == 0 {
return fmt.Errorf("no hosts given (pass as arguments or pipe node-lookup output)")
}
nodesURL := puppet.NodesEndpoint(cfg.PuppetDBURL)
now := time.Now()
for _, h := range hosts {
node, lookupErr := puppet.LookupNode(nodesURL, h)
when := formatWhen(node, lookupErr, relative, loc, now)
fmt.Printf("%s\t%s\t%s\n", h, when, puppet.HostPageURL(cfg.PuppetboardURL, h))
}
return nil
},
}
f := root.Flags()
f.BoolVarP(&relative, "relative", "r", false, "Show the report time as a relative age (e.g. '3h ago')")
f.StringVarP(&tz, "timezone", "z", "", "IANA timezone for the report time (e.g. Asia/Singapore); default local")
f.StringVar(&boardURL, "puppetboard-url", cfg.PuppetboardURL, "Puppetboard base URL (overrides config and NODE_LOOKUP_PUPPETBOARD_URL)")
f.StringVar(&pdbURL, "url", cfg.PuppetDBURL, "PuppetDB facts URL (overrides config and NODE_LOOKUP_URL)")
root.AddCommand(&cobra.Command{
Use: "version",
Short: "Print the version",
Run: func(cmd *cobra.Command, args []string) { fmt.Println(version) },
SilenceUsage: true,
})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}