Files
puppet-catalog-tools/AGENTS.md
T
unkin-agent 8093933500
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
add the catalog-impact binary
Work out which hosts a puppet-prod change can affect, so a catalog test can
fan out over exactly those hosts instead of the whole estate.

- add cmd/catalog-impact and internal/impact
- map hieradata paths onto fact selectors from the hiera hierarchy
- trace manifest classes back to role classes through include, contain,
  class {} and hiera_include, to a fixed point
- fetch the estate fact table from pdbmux in one query via FactsByName
- widen to all hosts, loudly, for any path that matches no rule
- ship the binary and its completions in the RPM
2026-09-12 16:15:00 +10:00

4.0 KiB

AGENTS.md

Project Overview

Go CLI tooling for the Puppet estate, shipped as one RPM (puppet-catalog-tools). Every tool is a separate main package under cmd/; shared code lives in internal/.

  • catalog-diff — compiles a host's catalog twice (base environment and the environment built from the branch under test) over the puppetserver API and diffs the result.
  • catalog-impact — maps a control repo change onto the hosts it can affect, by reading hieradata paths as hierarchy positions and tracing manifest classes backwards to role classes.

Structure

cmd/catalog-diff/       # the CLI (flags, exit codes, orchestration)
cmd/catalog-impact/     # the CLI (flags, output modes)
internal/catalog/       # catalog types, normalisation, diff engine, renderers,
                        # branch.go (BranchToEnvironment)
internal/puppetserver/  # mTLS client for POST /puppet/v4/catalog + /status/v1/simple
internal/impact/        # path -> selector rules, repo index, reverse lookup,
                        # estate fact table, renderers
internal/pdbmux/        # unauthenticated fact client for pdbmux
internal/config/        # XDG config file + env var loading
packaging/nfpm.yaml     # RPM spec (envsubst-templated)
scripts/build-rpm.sh    # completions + nfpm packaging
.woodpecker/            # CI: build, test, pre-commit (PR) + release (tag)

Adding a binary: create cmd/<name>/, add the name to BINARIES in the Makefile and to scripts/build-rpm.sh, and add its files to packaging/nfpm.yaml.

Transport

  • Catalogs: POST /puppet/v4/catalog with persistence off, options.prefer_requested_environment: true, facts and synthesised trusted facts in the body. Auth is client mTLS against the estate Puppet CA; the certname must be allowed in the puppetserver auth.conf.
  • Facts: GET /pdb/query/v4/nodes/<certname>/facts on pdbmux, falling back to /pdb/query/v4/factsets/<certname>. No auth, no client cert.
  • Estate-wide facts: one AST query against /pdb/query/v4/facts (pdbmux.Client.FactsByName). The estate is ~150 nodes, so catalog-impact pulls the whole table once and filters in Go rather than querying per host.
  • The two services have different auth models and stay separate clients.

Gotchas

  • An ENC-supplied environment outranks the requested one. Every compile checks the returned catalog's environment and hard-fails on a mismatch — never relax that check.
  • Branch names are converted the way g10k does it (\W -> _, case preserved). The rule lives in one function, catalog.BranchToEnvironment.
  • Exit codes are a contract: 0 same, 1 compile failed, 2 differences, 3 transport/auth/mismatch/usage. --testonly never returns 2.
  • Normalisation carries a pile of false-positive fixes (classes and exported resources skipped, before/require dropped, notify/subscribe sorted, File titles keyed by path, datatype-only changes suppressed). Removing one floods the output with noise.

catalog-impact gotchas

  • The reverse lookup must search hieradata as well as manifests. profiles::base declares ($hiera_include - $hiera_exclude).include, and some modules — the jellyfin one, for instance — are applied only by a hiera_include list in a role yaml. A manifest-only search reports them as orphans.
  • hieradata/roles/<t1>/<t2>.yaml is a prefix match on the role tiers, not an exact one. site/roles/manifests/.../<t3>.pp is exact.
  • The bias is to over-include: an unmapped path widens to the whole estate and says so on stderr. Never turn a widening into a silent empty result.
  • The repo index comes from the --repo checkout, not from --branch. CI runs on the branch checkout, so the two agree there.

Testing

make test     # go test -v -race ./...

Everything is stubbed with httptest (a TLS server for puppetserver, a plain one for pdbmux) — no live estate access is required. Renderer output is covered by golden files under internal/catalog/testdata; regenerate with go test ./internal/catalog -update.