Skip to content

Documentation gap: Provider-specific annotations placement for Gateway API sources #5901

@lexfrei

Description

@lexfrei

Documentation gap: Provider-specific annotations placement for Gateway API sources

Issue Summary

The annotations documentation states that the "Gateway" source supports provider-specific annotations, but it doesn't clarify which resource (Gateway vs HTTPRoute/other Routes) these annotations should be placed on.

Current Behavior

According to the source code (source/gateway.go), annotations are handled as follows:

Gateway resource annotations:

  • external-dns.alpha.kubernetes.io/target - read from Gateway (line ~380)

HTTPRoute (and other Route) resource annotations:

  • external-dns.alpha.kubernetes.io/hostname - read from Route
  • external-dns.alpha.kubernetes.io/ttl - read from Route
  • Provider-specific annotations (e.g., cloudflare-proxied, aws/*, etc.) - read from Route (line ~244)

This means:

// In Endpoints() function:
annots := meta.Annotations  // <- HTTPRoute annotations
providerSpecific, setIdentifier := annotations.ProviderSpecificAnnotations(annots)

While targets come from Gateway:

// In resolve() function:
override := annotations.TargetsFromTargetAnnotation(gw.gateway.Annotations)

Example That Doesn't Work

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cilium-gateway
  namespace: kube-system
  annotations:
    external-dns.alpha.kubernetes.io/target: "198.51.100.1"  # ✅ Works
    external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"  # ❌ Ignored
spec:
  gatewayClassName: cilium
  listeners:
    - name: https-example
      hostname: "example.com"
      protocol: HTTPS
      port: 443
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example
  annotations: {}  # Provider-specific annotations needed here!
spec:
  parentRefs:
    - name: cilium-gateway
      namespace: kube-system
  hostnames:
    - example.com
  rules:
    - backendRefs:
        - name: example-service
          port: 80

Correct Configuration

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cilium-gateway
  namespace: kube-system
  annotations:
    external-dns.alpha.kubernetes.io/target: "198.51.100.1"  # ✅ On Gateway
spec:
  gatewayClassName: cilium
  listeners:
    - name: https-example
      hostname: "example.com"
      protocol: HTTPS
      port: 443
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example
  annotations:
    external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"  # ✅ On HTTPRoute
spec:
  parentRefs:
    - name: cilium-gateway
      namespace: kube-system
  hostnames:
    - example.com
  rules:
    - backendRefs:
        - name: example-service
          port: 80

Related Issues

  • Question: Gatway-api target on HTTPRoute #4056 - mentions that target annotation "must be on the Gateway, so it won't work on HTTPRoute" but doesn't clarify other annotations
  • Annotations table uses "Gateway" as source name, which is ambiguous (refers to gateway-api sources, not the Gateway resource itself)

Proposed Solutions

Option 1: Improve Documentation (Quick Fix)

Update the documentation to explicitly state annotation placement:

In docs/annotations/annotations.md:

Add a new section or table explaining:

Annotation Type Gateway Resource HTTPRoute Resource GRPCRoute/TLSRoute/etc
target ✅ Read from Gateway ❌ Ignored ❌ Ignored
hostname ❌ Not used ✅ Read from Route ✅ Read from Route
ttl ❌ Not used ✅ Read from Route ✅ Read from Route
Provider-specific (cloudflare-proxied, aws/*, etc.) ❌ Not used ✅ Read from Route ✅ Read from Route

In docs/sources/gateway-api.md:

Add examples showing correct annotation placement for different scenarios.

Option 2: Support Annotations on Both Resources (Feature Enhancement)

Implement annotation merging logic where:

  1. Gateway annotations apply to all Routes using that Gateway (defaults)
  2. Route annotations override Gateway annotations for specific Routes
  3. This would match user expectations and provide better DX

Example code change in source/gateway.go:

// Merge Gateway and Route annotations
gwAnnots := gw.gateway.Annotations
rtAnnots := meta.Annotations
mergedAnnots := mergeAnnotations(gwAnnots, rtAnnots) // Route overrides Gateway

providerSpecific, setIdentifier := annotations.ProviderSpecificAnnotations(mergedAnnots)

This would enable use cases like:

  • Setting cloudflare-proxied: true on Gateway (applies to all routes)
  • Overriding with cloudflare-proxied: false on specific HTTPRoute

Questions for Maintainers

  1. Is the current behavior (provider-specific annotations only on Routes) intentional design or unintended limitation?
  2. If intentional: Can we improve documentation to make this clear?
  3. If limitation: Would you accept a PR implementing Option 2 (annotation merging)?

Environment

  • External-DNS version: v0.19.0
  • DNS provider: Cloudflare
  • Source: gateway-httproute
  • Kubernetes Gateway API: v1

Additional Context

This behavior is architecturally logical from Gateway API perspective:

  • Gateway = infrastructure layer (IP addresses, listeners)
  • Routes = application layer (DNS records, routing rules)

However, it's not intuitive for users and causes confusion when annotations work on Gateway for some fields (target) but not others (provider-specific).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions