// 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 }