# ============================================================================= # L2 BEHAVIORAL — DNS auto-record / exposure (R-DNS-02) [RUNNABLE] # Catalog: docs/14 "DNS/exposure (R-DNS-*)" # Spec: docs/09 (ExternalDNS auto-manages records; split-horizon foundation) # # GIVEN a LoadBalancer Service annotated with an external-dns hostname, # WHEN ExternalDNS reconciles (source=service, provider=cloudflare, policy=sync), # THEN a PUBLIC DNS record for that hostname is auto-created, pointing at the # Service's Cilium-assigned LB IP (R-DNS-02). The record resolves to the # cluster LB IP (10.73.0.240-.250), i.e. clients reach the cluster endpoint. # # CLEANUP: chainsaw deletes the per-test namespace (Service included) at teardown; # ExternalDNS policy=sync then REMOVES the record from Cloudflare on its next pass. # This test creates a real record in the live keithjackson.dev zone (operator-approved). # # SCOPE: asserts R-DNS-02 (auto-managed record appears) concretely. R-DNS-01/03 # (split-horizon internal-vs-external resolution) need the internal resolver and # are covered separately. # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: dns-exposure labels: conformance.home.k3s/R-DNS-02: "true" annotations: conformance.home.k3s/requirements: "R-DNS-02" conformance.home.k3s/layer: "L2" conformance.home.k3s/safety-critical: "false" conformance.home.k3s/executable: "true" conformance.home.k3s/intent: >- A LoadBalancer Service annotated with an external-dns hostname gets a public DNS record auto-created by ExternalDNS (Cloudflare), resolving to its LB IP. spec: # ExternalDNS reconcile + Cloudflare propagation can take a couple of minutes; # widen the assert/exec windows for this test only. timeouts: apply: 90s assert: 5m exec: 6m steps: - name: given-a-service-requesting-a-dns-name try: - apply: resource: apiVersion: v1 kind: Service metadata: name: dns-probe-svc annotations: external-dns.alpha.kubernetes.io/hostname: conformance-dns.home.keithjackson.dev spec: type: LoadBalancer # No real backend needed — Cilium LB-IPAM assigns a VIP regardless, and # ExternalDNS creates the record from the Service's LB IP + annotation. selector: app: dns-probe-no-such-backend ports: - name: http port: 80 targetPort: 80 - name: then-externaldns-creates-the-public-record try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: dns-verify spec: backoffLimit: 2 activeDeadlineSeconds: 330 template: spec: restartPolicy: Never containers: - name: probe image: busybox:1.36.1 command: - sh - -c - | set -u host="conformance-dns.home.keithjackson.dev" # Poll PUBLIC DNS (1.1.1.1) until the auto-created A record appears and # points into the cluster LB range 10.73.0.24x. Proves ExternalDNS created it. for i in $(seq 1 28); do ip="$(nslookup "$host" 1.1.1.1 2>/dev/null | grep -oE '10\.73\.0\.[0-9]+' | head -1)" if [ -n "$ip" ]; then echo "RECORD_FOUND $host -> $ip"; exit 0; fi echo "poll $i: record not yet published"; sleep 10 done echo "FAIL: ExternalDNS did not publish the record within timeout"; exit 1 - assert: resource: apiVersion: batch/v1 kind: Job metadata: { name: dns-verify } status: { succeeded: 1 } finally: - script: content: 'echo "teardown: chainsaw deletes the namespace/Service; ExternalDNS policy=sync removes the Cloudflare record"'