Skip to content

chore(nginx-gateway-fabric): take the chart to 2.7.0 - #443

Merged
jdwillmsen merged 1 commit into
mainfrom
chore/nginx-gateway-fabric-2.7.0
Sep 8, 2026
Merged

chore(nginx-gateway-fabric): take the chart to 2.7.0#443
jdwillmsen merged 1 commit into
mainfrom
chore/nginx-gateway-fabric-2.7.0

Conversation

@jdwlabs-agent-bot

Copy link
Copy Markdown
Contributor

Takes the nginx-gateway-fabric chart from 2.6.7 to 2.7.0. Split out of #417 alongside #442 (argo-cd) so each lands on its own.

Merge this in a window where transient per-node connection failures are acceptable. See "What it disrupts" — this is the only part of either split that can drop live traffic. Land #442 first.

The parked concern, and why it does not apply

This bump sat parked on the grounds that 2.7.0 needs Gateway API v1 TCPRoute/UDPRoute CRDs that this cluster does not have. The CRD gap is real; the conclusion is inverted.

The cluster is already in the degraded-but-working state, today, on 2.6.7. bootstrap/crds/foundation-crds.yaml vendors 8 Gateway API CRDs, every one annotated bundle-version: v1.1.0, channel: experimental, owned by the platform-crds Application at sync-wave -1. 2.6.x recommends v1.5.1. So:

$ kubectl get gatewayclass nginx -o jsonpath=...
SupportedVersion=False  reason=UnsupportedVersion
  msg=The Gateway API CRD versions are not recommended. Recommended version is v1.5.1
Accepted=True   ResolvedRefs=True

NGF's validateCRDVersions compares major first and only flags a minor mismatch as bestEffort:

if version.major != supportedAPIVersion.major { unsupported = true } else
if version.minor != supportedAPIVersion.minor { bestEffort = true }

v1.1.0 against 2.7.0's recommended v1.6.1 is the same arithmetic as v1.1.0 against v1.5.1 — major matches, bestEffort. 2.7.0 changes the version this condition names and nothing else. Gateways stay Accepted and Programmed.

TCPRoute and UDPRoute

2.7.0 moves them out of the gwAPIExperimentalFeatures block and gates them on the v1 CRD being served instead. Live discovery:

/apis/gateway.networking.k8s.io/v1       → GRPCRoute, Gateway, GatewayClass, HTTPRoute
/apis/gateway.networking.k8s.io/v1alpha2 → GRPCRoute, ReferenceGrant, TCPRoute, TLSRoute, UDPRoute
/apis/gateway.networking.k8s.io/v1beta1  → Gateway, GatewayClass, HTTPRoute, ReferenceGrant

The Kind is absent at v1, so CRD discovery filters those controllers out of the registration list and their ObjectLists never enter the cache. No informer, no error, no crash.

Nothing is lost, because they are not watched today either — the control-plane Deployment carries no --gateway-api-experimental-features flag, and there is nothing to watch:

$ kubectl get tcproutes,udproutes,tlsroutes -A   → No resources found (all three)
$ kubectl get httproutes -A                      → 24
$ kubectl get grpcroutes -A                      → 0

ReferenceGrant behaves identically in both versions: the v1 controller is gated on v1 presence (absent), and the object list falls back to gatewayv1beta1.ReferenceGrantList. The per-namespace allow-gateway-tls grants keep working exactly as they do now.

Rendered chart delta with this tenant's values

  • images 2.6.7 → 2.7.0 on the control-plane Deployment and, via NginxProxy, the data-plane DaemonSet
  • two new controller args, both equal to the current implicit defaults: --server-tls-domain=svc, --cluster-domain=cluster.local
  • new ClusterRole grants: services/status patch, policy/poddisruptionbudgets, and list/watch/status on tcproutes and udproutes
  • a metrics port 9113 on the control-plane ClusterIP Service (the PodMonitor scrapes pods, so no conflict)
  • cert-generator SA/Role/RoleBinding/SCC gain pre-upgrade alongside pre-install, plus a hook-delete-policy. The Job itself already had both. certGenerator.overwrite stays false, so no certificate churn
  • two new chart CRDs, externalloadbalancers and payloadprocessors, and additive schema fields on the existing gateway.nginx.org CRDs. skipCrds is not set in the tenant envelope and Argo CD already tracks all 11 of these CRDs as Synced resources, so they land automatically — no manual CRD apply step
  • Chart.yaml kubeVersion floor moves to >=1.32.0-0; the cluster is v1.36.3

What it disrupts

The data-plane DaemonSet is created by the NGF controller, not by Helm, and rolls after the control plane comes up:

updateStrategy: RollingUpdate, maxSurge: 0, maxUnavailable: 1   (5 pods)
Service: NodePort, externalTrafficPolicy: Local   (30180 / 30543)

maxSurge: 0 with externalTrafficPolicy: Local means each node's NodePort refuses connections while that node's pod restarts, one node at a time. Whatever fronts these five NodePorts has to health-check the draining node out, or the roll surfaces as transient connection failures. There is also a short window where a 2.7.0 control plane drives a 2.6.7 data plane.

Watch kubectl -n nginx-gateway rollout status ds/platform-gateway-nginx through all five pods.

Deliberately not included

Moving the Gateway API bundle v1.1.0 → v1.6.1 is a five-minor jump that changes served and storage versions on referencegrants (currently v1beta1 storage) and needs its own storage-version-migration plan and a freshness check. It is not a prerequisite for this upgrade: the SupportedVersion=False condition predates this PR and simply continues to exist.

Rollback

Revert the one line and let Argo CD sync; prune: true removes the two new CRDs and no custom resources of those kinds exist. The data plane rolls back the same way. If the control plane is wedged and cannot reconcile, kubectl -n nginx-gateway rollout undo deploy/platform-nginx-gateway-fabric restores 2.6.7 immediately — the existing DaemonSet keeps serving traffic throughout, since it does not depend on the control-plane pod.

Worth an eye on the first sync

Whether Argo CD deletes the cert-generator hook SA/Role/RoleBinding at PreSync-phase completion rather than immediately. This was reasoned from the hook policies rather than observed; if deletion were immediate, the cert-generator Job could lose its RBAC mid-run.

🤖 Generated with Claude Code

The Gateway API CRD bundle here is older than either release recommends, so
the GatewayClass already reports SupportedVersion=False; 2.7.0 only changes
which version that message names. TCPRoute and UDPRoute move to a v1 CRD
check that this cluster does not satisfy, which drops controllers that were
never watched here anyway.

Merging rolls all five data-plane pods one node at a time behind a NodePort
service with externalTrafficPolicy Local, so each node refuses connections
while its pod restarts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@jdwillmsen jdwillmsen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on jdwillmsen's instruction, posted by an agent session on their behalf.

The parked CRD concern does not block: this cluster already runs Gateway API bundle v1.1.0 against a recommended v1.5.1 and the GatewayClass already reports SupportedVersion=False; NGF compares major only, so v1.6.1 lands in the same bestEffort bucket and only the message string changes. TCPRoute/UDPRoute gate on a v1 CRD that is absent here, so those controllers are simply not registered — and they are not watched today either (no experimental-features flag, zero TCP/UDP/TLS routes). ReferenceGrant falls back to v1beta1 identically in both versions.

Accepted knowingly: the data-plane DaemonSet rolls all 5 pods at maxSurge: 0 behind a NodePort service with externalTrafficPolicy: Local, so each node refuses connections on 30180/30543 while its pod restarts. Merging now with the roll watched. 19/19 checks green.

@jdwillmsen
jdwillmsen merged commit a968a75 into main Sep 8, 2026
19 checks passed
@jdwillmsen
jdwillmsen deleted the chore/nginx-gateway-fabric-2.7.0 branch September 8, 2026 06:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant