Skip to content

prod pdns backend returns 422 on PATCH /zones; new DNSZones never Accepted #51

Description

@ecv

Summary

Prod dns-operator cannot program a cohort of DNSZones. The manager container
of the dns-operator-agent-pdns-auth-0 StatefulSet (--role=downstream)
sends PowerDNS PATCH /api/v1/servers/localhost/zones/* payloads containing
duplicate records[] within a single RRset, and pdns-auth rejects the whole
RRset with HTTP 422. Affected zones never reach Accepted=true /
Programmed=true. Well-formed existing zones are unaffected.

Root cause is operator-side payload construction, not the pdns backend. pdns
is rejecting invalid input correctly.

Surfaced via the infra-repo scheduled Integration Chainsaw suite, whose
dns-setup test has failed on every run since ~2026-07-03 20:00 UTC. The test
creates a fresh e2e-dns-<ts>.datumchainsaw.art zone and waits 5m for
Accepted=true; it times out at the 5m mark because the zone's default NS
RRset carries duplicate records
(see Mode 1 below), which fails on every
newly-created zone.

The 422 rejection reasons (captured live, 2026-07-05 17:10–17:11Z)

Source (corrects the original "pdns logs length only" note — the operator logs
the full body):

kubectl -n datum-dns-system logs dns-operator-agent-pdns-auth-0 -c manager \
  | grep "pdns apply failed"

Three distinct failure modes, one common cause (duplicate records[]):

Mode 1 — duplicate NS on the default RRset (the fleet-wide blocker, incl. Chainsaw):

{"error": "Multiple errors found in RRset", "errors": [
  "RRset estate-buddy.com. IN NS: duplicate record with content \"ns1.datumdns.net\"",
  "RRset estate-buddy.com. IN NS: duplicate record with content \"ns2.datumdns.net\"",
  "RRset estate-buddy.com. IN NS: duplicate record with content \"ns3.datumdns.net\"",
  "RRset estate-buddy.com. IN NS: duplicate record with content \"ns4.datumdns.net\""
]}

The default NS RRset the operator ensures on every new zone lists each
nameserver twice. Hits every newly-created zone, including the Chainsaw test.

Mode 2 — multi-valued / duplicate CNAME:

{"error": "Multiple errors found in RRset", "errors": [
  "RRset www.ibm.com. IN CNAME: only one such record allowed",
  "RRset www.ibm.com. IN CNAME: duplicate record with content \"outer-global-dual.ibmcom-tls12.edgekey.net\""
  // repeated ~7x
]}

Mode 3 — ALIAS conflicts with a pre-existing RRset:

status 422: {"error": "RRset www.ab.dk. IN ALIAS: Conflicts with pre-existing RRset"}

Also go.miloapis.com., www.alt-cloud.org., www.erupt.dev..

Why 2026-07-03 20:00, and why not the backend

  • The pdns container in dns-operator-agent-pdns-auth-0 has restartCount: 0
    since 2026-06-30 — the backend binary/config did not change at the onset.
    (Image is powerdns/pdns-auth-51:latest, a floating tag — worth pinning, but
    not the trigger.)
  • The onset lines up with a cohort of new zones (demo/import domains: ibm.com,
    ab.dk, erupt.dev, estate-buddy.com, in ns-<uuid> project namespaces)
    beginning to reconcile. Their record sets trip the duplicate/single-value/ALIAS
    rules. Pre-existing well-formed zones keep reconciling green.

Impact

  • Any DNSZone whose emitted payload contains a duplicate record (esp. the
    default NS RRset) fails to program in prod.
  • Blocks the infra Integration Chainsaw dns-setup test on every scheduled run.

Fix

Operator-side, in the RRset build path (internal/pdns):

  1. Dedupe records[] before PATCH — collapse identical content within an
    RRset. Fixes Mode 1 (NS) and Mode 2 (CNAME), including the Chainsaw blocker.
  2. Enforce single-value for CNAME/ALIAS before sending.
  3. ALIAS coexistence — don't emit ALIAS at a name that already holds another
    RRset (or clear the conflicting set first).

Also (separate, diagnosability): surface the specific pdns error string into
the DNSRecordSet status condition instead of collapsing all 422s to the generic
FriendlyMessage — this incident was invisible via kubectl get dnsrecordset -o yaml because the detail only reached the logs (internal/pdns/errors.go).

Cross-ref: infra Integration Chainsaw Failure Tracker (datum-cloud/infra#2879).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions