- unwrap the {"catalog": ...} envelope, still accepting a bare catalog
- report an environment-less response as a protocol mismatch, not an ENC problem
- make Resource.Line a pointer and Resource.Params() map absent onto empty
- render a missing manifest location as "(source unknown)"
- retry compile failures caused by the compiler's PuppetDB query
- stop retrying manifest errors the compiler returns as HTTP 500
- add a trimmed real-server response fixture and tests for each shape
3.7 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.
Structure
cmd/catalog-diff/ # the CLI (flags, exit codes, orchestration)
internal/catalog/ # catalog types, normalisation, diff engine, renderers,
# branch.go (BranchToEnvironment)
internal/puppetserver/ # mTLS client for POST /puppet/v4/catalog + /status/v1/simple
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/catalogwithpersistenceoff,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 puppetserverauth.conf. - Facts:
GET /pdb/query/v4/nodes/<certname>/factson pdbmux, falling back to/pdb/query/v4/factsets/<certname>. No auth, no client cert. - The two services have different auth models and stay separate clients.
Gotchas
- The compiler wraps its response as
{"catalog": {...}}.decodeCatalogunwraps it and still accepts a bare catalog; decoding straight intocatalog.Catalogsilently yields an empty one. - Only
type,title,tagsandexportedare present on every resource.parametersis absent on class/stage/anchor resources andfile/lineon resources with no manifest location, soResource.Lineis a pointer andResource.Params()maps absent onto empty. - An ENC-supplied environment outranks the requested one. Every compile checks
the returned catalog's
environmentand hard-fails on a mismatch — never relax that check. A returned environment that is empty is a decode failure, not a mismatch, and is reported as such. - The compiler returns HTTP 500 for manifest errors too, so retryability is
decided from the error text: a failed PuppetDB query (
server_urls,/pdb/query/) is transient, a manifest error never is. - 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.
--testonlynever returns 2. - Normalisation carries a pile of false-positive fixes (classes and exported
resources skipped,
before/requiredropped,notify/subscribesorted, File titles keyed bypath, datatype-only changes suppressed). Removing one floods the output with noise.
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.
internal/puppetserver/testdata/catalog_response.json is a trimmed capture of
a real OpenVoxServer 8.8 response, kept for its shape: the catalog envelope
and resources that omit parameters, file and line. Sensitive values are
replaced.