Skip to content

feat: ngf http options via snippet filters - #429

Open
cloud-j-luna wants to merge 3 commits into
mainfrom
feat/ngf-http-options-via-snippets
Open

feat: ngf http options via snippet filters#429
cloud-j-luna wants to merge 3 commits into
mainfrom
feat/ngf-http-options-via-snippets

Conversation

@cloud-j-luna

@cloud-j-luna cloud-j-luna commented Aug 5, 2026

Copy link
Copy Markdown
Member

Providers to create SnippetFilter for options on Gateway providers

Test evidence

1. All filters emitted and reaching the data plane. The provider's SnippetsFilter and the rendered nginx include are identical, and nginx -t accepts them:

$ nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is successful

$ cat .../SnippetsFilter_http.server.location_<ns>_hello.localhost.conf
client_max_body_size 2097152;
proxy_read_timeout 60000ms;
proxy_send_timeout 60000ms;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_next_upstream_tries 3;
proxy_next_upstream off;

2. Programmed and resolved (no dangling reference).

HTTPRoute hello.localhost:  Accepted=True  ResolvedRefs=True
SnippetsFilter hello.localhost: Accepted=True
Gateway akash-gateway:  Accepted=True  Programmed=True

3. client_max_body_size enforced end to end (curl through the gateway).

[GET /]                 -> 200     # route serves
[POST 3MB > 2MB limit]  -> 413     # nginx rejects, per client_max_body_size
[POST 1MB < 2MB limit]  -> 405     # under limit: passed nginx to the backend (405 = app rejects POST)

nginx confirms the cause of the 413:

[error] client intended to send too large body: 3145728 bytes, server: hello.localhost, request: "POST / HTTP/1.1"

@cloud-j-luna
cloud-j-luna requested a review from a team as a code owner August 5, 2026 20:20
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

NGINX Gateway configuration now uses per-route SnippetsFilter resources instead of annotations. The gateway provider builds these resources, attaches extension references to HTTPRoute objects, applies them with owner references, and adds the required deployment arguments and RBAC permissions.

Changes

SnippetsFilter route extensions

Layer / File(s) Summary
Route extension pipeline
cluster/kube/gateway/gateway.go, cluster/kube/gateway/httproute.go, cluster/kube/gateway/httproute_test.go
GatewayProvider now builds route extensions. HTTPRoute reconciliation creates or updates owned extensions before it publishes filter references. Tests cover success and failure paths.
NGINX SnippetsFilter generation
cluster/kube/gateway/nginx_gateway.go, cluster/kube/gateway/nginx_gateway_test.go, cluster/kube/client_gateway_test.go, integration/gateway_api_test.go
HTTP options render into per-route SnippetsFilter resources. HTTPRoutes reference the filter when directives exist. Tests cover rendering, validation, metadata, references, and removal of legacy annotations.
Gateway Fabric enablement
_docs/kustomize/nginx-gateway-fabric/kustomization.yaml, _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
Kustomize enables --snippets-filters and adds permissions for snippetsfilters resources.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HTTPRouteReconciliation
  participant GatewayProvider
  participant nginxGateway
  participant KubernetesAPI
  HTTPRouteReconciliation->>GatewayProvider: BuildRouteExtensions(...)
  GatewayProvider->>nginxGateway: BuildRouteExtensions(...)
  nginxGateway-->>GatewayProvider: SnippetsFilter resource
  HTTPRouteReconciliation->>KubernetesAPI: Create or update SnippetsFilter
  HTTPRouteReconciliation->>KubernetesAPI: Publish HTTPRoute with ExtensionRef
Loading

Possibly related PRs

Suggested reviewers: troian

Poem

A rabbit builds filters with care,
Route references point to them there.
Directives are stored,
RBAC is restored,
And NGINX applies the right share.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description explains the SnippetsFilter implementation and provides relevant end-to-end test evidence.
Title check ✅ Passed The title clearly summarizes adding HTTP options support through NGINX Gateway Fabric SnippetsFilters.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ngf-http-options-via-snippets

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cluster/kube/gateway/httproute.go`:
- Around line 113-114: Update the route reconciliation flow around
applyRouteExtensions to apply SnippetsFilter before publishing its ExtensionRef:
for existing routes, pass existing.GetUID() and apply the extension before
updating the HTTPRoute; for new routes, create the route without the filter,
apply the extension, then add the filter reference only after success.

In `@cluster/kube/gateway/nginx_gateway.go`:
- Around line 189-198: Update the NextCases rendering in the raw
location-context SnippetsFilter to validate each value against a closed set of
permitted proxy_next_upstream tokens before appending it to cases. Continue
normalizing valid HTTP status codes to http_ prefixed tokens, but omit or reject
all other values, including semicolons, newlines, and unknown tokens, so only
safe values reach strings.Join and line.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd48570b-d0ae-42c8-99b3-344b7da2cb22

📥 Commits

Reviewing files that changed from the base of the PR and between e70d473 and 393b3b3.

📒 Files selected for processing (6)
  • _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
  • _docs/kustomize/nginx-gateway-fabric/kustomization.yaml
  • cluster/kube/gateway/gateway.go
  • cluster/kube/gateway/httproute.go
  • cluster/kube/gateway/nginx_gateway.go
  • cluster/kube/gateway/nginx_gateway_test.go

Comment thread cluster/kube/gateway/httproute.go Outdated
Comment thread cluster/kube/gateway/nginx_gateway.go Outdated
@cloud-j-luna
cloud-j-luna force-pushed the feat/ngf-http-options-via-snippets branch from 393b3b3 to 3a4f8dd Compare August 5, 2026 20:24
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@cloud-j-luna
cloud-j-luna force-pushed the feat/ngf-http-options-via-snippets branch from 3a4f8dd to 4265e8f Compare August 5, 2026 20:49
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
cluster/kube/gateway/nginx_gateway.go (2)

147-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Process-global viper state leaks into the builders and into their tests. proxyBufferSize reads a process-global viper key from inside otherwise pure builder functions, so both the builders and the tests that assert "no snippet" depend on ambient global state and on test order.

  • cluster/kube/gateway/nginx_gateway.go#L147-L152: read providerflags.FlagProxyBufferSize once in NewNginxGateway, store it on nginxGateway, and pass it to httpOptionsSnippet from BuildHTTPRouteSpec and BuildRouteExtensions.
  • cluster/kube/gateway/nginx_gateway_test.go#L81-L84: construct the provider with an explicit empty buffer size instead of relying on viper being unset, and apply the same change in TestBuildHTTPRouteSpecExtensionRef.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cluster/kube/gateway/nginx_gateway.go` around lines 147 - 152, Remove
process-global viper access from the builders: have NewNginxGateway read
providerflags.FlagProxyBufferSize once, store it on nginxGateway, and pass that
value to httpOptionsSnippet from BuildHTTPRouteSpec and BuildRouteExtensions. In
cluster/kube/gateway/nginx_gateway_test.go lines 81-84 and
TestBuildHTTPRouteSpecExtensionRef, construct the provider with an explicit
empty buffer size.

67-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Compute the snippet once and derive both the filter reference and the object from it.

BuildHTTPRouteSpec and BuildRouteExtensions both call httpOptionsSnippet(directive, proxyBufferSize()) and both branch on the result being empty. The two decisions must agree. If they diverge later, the route publishes an ExtensionRef with no matching SnippetsFilter, and NGF sets ResolvedRefs=False and serves HTTP 500 for that rule. Consider computing the snippet once per reconcile and passing it to both paths, or deriving the filter reference from the extension list that BuildRouteExtensions returns.

Also applies to: 121-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cluster/kube/gateway/nginx_gateway.go` around lines 67 - 79, Compute the
httpOptionsSnippet result once in the shared route-building flow and reuse it
for both BuildHTTPRouteSpec and BuildRouteExtensions. Ensure the filter
reference is created exactly when the corresponding snippet object is included,
so both paths make the same empty/non-empty decision and never publish an
unmatched ExtensionRef.
cluster/kube/gateway/httproute.go (2)

186-191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the extension GVR from the provider instead of pluralizing the kind.

strings.ToLower(gvk.Kind) + "s" works for SnippetsFilter, but it is not a valid pluralization rule for Kubernetes resources in general. A future extension kind such as Policy, Ingress, or ObservabilityPolicy maps to a wrong resource name and the write fails at runtime. Let GatewayProvider return the schema.GroupVersionResource next to each object, or use a RESTMapper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cluster/kube/gateway/httproute.go` around lines 186 - 191, The GVR
construction in the extension handling path must not pluralize gvk.Kind
manually. Update the GatewayProvider integration to obtain the correct
schema.GroupVersionResource for each extension object, or resolve it through a
RESTMapper, and use that value when performing the write while preserving the
existing group and version context.

86-86: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Delete a stale extension when the route no longer needs one.

BuildRouteExtensions returns nil when the directive carries no http_options. On a reconcile that removes all options, exts is empty, the updated route drops its ExtensionRef, and the previously created SnippetsFilter stays in the namespace until the route is deleted. The leftover object is inert, but it hides the current configuration state. Consider deleting extension objects that the route no longer references.

Also applies to: 137-139

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cluster/kube/gateway/httproute.go` at line 86, Update the reconciliation
logic around BuildRouteExtensions and the route update handling to delete
previously created SnippetsFilter extension objects that are no longer
referenced when exts is nil or empty. Ensure cleanup occurs when http_options
are removed, while preserving existing creation and reference behavior for
routes that still require extensions.
cluster/kube/gateway/httproute_test.go (1)

111-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the new-route failure path.

This test pre-creates the HTTPRoute, so routeExists is true and the bare-create branch never runs. The riskier path is a brand-new route: CreateOrUpdateHTTPRoute creates the route without filters, then the extension apply fails. Add a case without a pre-existing route that asserts the persisted route carries no ExtensionRef and that the observer sees the create outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cluster/kube/gateway/httproute_test.go` around lines 111 - 137, The existing
TestCreateOrUpdateHTTPRouteDoesNotDangleOnExtensionFailure covers only the
update path because it pre-creates the HTTPRoute. Add a separate test for a
brand-new route that invokes CreateOrUpdateHTTPRoute with no existing route,
forces SnippetsFilter creation to fail, and verifies the persisted route has no
ExtensionRef while NoopHTTPRouteObserver or the appropriate observer records the
route-create outcome.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cluster/kube/gateway/httproute.go`:
- Around line 127-139: Update the applyRouteExtensions failure path to notify
httpRouteMetricsObserver via observer.OnCreate only when the bare HTTPRoute was
created in this operation, using createdBare to guard the callback. Preserve the
existing successful-create callback flow and avoid adding an unconditional
notification that would double-count routes after the final update.

In `@cluster/kube/gateway/nginx_gateway_test.go`:
- Around line 81-84: Make the no-snippet assertions deterministic by explicitly
setting providerflags.FlagProxyBufferSize to an empty value through viper before
exercising BuildRouteExtensions and the corresponding
TestBuildHTTPRouteSpecExtensionRef case. Add the required viper and
providerflags imports, while preserving the existing assertions and test
behavior.

---

Nitpick comments:
In `@cluster/kube/gateway/httproute_test.go`:
- Around line 111-137: The existing
TestCreateOrUpdateHTTPRouteDoesNotDangleOnExtensionFailure covers only the
update path because it pre-creates the HTTPRoute. Add a separate test for a
brand-new route that invokes CreateOrUpdateHTTPRoute with no existing route,
forces SnippetsFilter creation to fail, and verifies the persisted route has no
ExtensionRef while NoopHTTPRouteObserver or the appropriate observer records the
route-create outcome.

In `@cluster/kube/gateway/httproute.go`:
- Around line 186-191: The GVR construction in the extension handling path must
not pluralize gvk.Kind manually. Update the GatewayProvider integration to
obtain the correct schema.GroupVersionResource for each extension object, or
resolve it through a RESTMapper, and use that value when performing the write
while preserving the existing group and version context.
- Line 86: Update the reconciliation logic around BuildRouteExtensions and the
route update handling to delete previously created SnippetsFilter extension
objects that are no longer referenced when exts is nil or empty. Ensure cleanup
occurs when http_options are removed, while preserving existing creation and
reference behavior for routes that still require extensions.

In `@cluster/kube/gateway/nginx_gateway.go`:
- Around line 147-152: Remove process-global viper access from the builders:
have NewNginxGateway read providerflags.FlagProxyBufferSize once, store it on
nginxGateway, and pass that value to httpOptionsSnippet from BuildHTTPRouteSpec
and BuildRouteExtensions. In cluster/kube/gateway/nginx_gateway_test.go lines
81-84 and TestBuildHTTPRouteSpecExtensionRef, construct the provider with an
explicit empty buffer size.
- Around line 67-79: Compute the httpOptionsSnippet result once in the shared
route-building flow and reuse it for both BuildHTTPRouteSpec and
BuildRouteExtensions. Ensure the filter reference is created exactly when the
corresponding snippet object is included, so both paths make the same
empty/non-empty decision and never publish an unmatched ExtensionRef.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f961c3c1-4561-49d8-b890-2777a27c61b5

📥 Commits

Reviewing files that changed from the base of the PR and between e70d473 and 4265e8f.

📒 Files selected for processing (8)
  • _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
  • _docs/kustomize/nginx-gateway-fabric/kustomization.yaml
  • cluster/kube/client_gateway_test.go
  • cluster/kube/gateway/gateway.go
  • cluster/kube/gateway/httproute.go
  • cluster/kube/gateway/httproute_test.go
  • cluster/kube/gateway/nginx_gateway.go
  • cluster/kube/gateway/nginx_gateway_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • cluster/kube/gateway/gateway.go
  • _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
  • _docs/kustomize/nginx-gateway-fabric/kustomization.yaml

Comment thread cluster/kube/gateway/httproute.go Outdated
Comment thread cluster/kube/gateway/nginx_gateway_test.go
@cloud-j-luna
cloud-j-luna force-pushed the feat/ngf-http-options-via-snippets branch from 4265e8f to 221cc8c Compare August 6, 2026 10:25
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cluster/kube/client_gateway_test.go`:
- Around line 50-53: Extend the ExtensionRef assertions in the gateway test to
verify its Group field matches the expected SnippetsFilter API group configured
by BuildHTTPRouteSpec, alongside the existing Kind and Name checks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2440e943-a8e7-4ae7-8791-74096047fed1

📥 Commits

Reviewing files that changed from the base of the PR and between e70d473 and 221cc8c.

📒 Files selected for processing (8)
  • _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
  • _docs/kustomize/nginx-gateway-fabric/kustomization.yaml
  • cluster/kube/client_gateway_test.go
  • cluster/kube/gateway/gateway.go
  • cluster/kube/gateway/httproute.go
  • cluster/kube/gateway/httproute_test.go
  • cluster/kube/gateway/nginx_gateway.go
  • cluster/kube/gateway/nginx_gateway_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • cluster/kube/gateway/gateway.go
  • _docs/kustomize/akash-operator-hostname/cluster-roles.yaml
  • cluster/kube/gateway/httproute.go
  • cluster/kube/gateway/nginx_gateway_test.go
  • _docs/kustomize/nginx-gateway-fabric/kustomization.yaml
  • cluster/kube/gateway/nginx_gateway.go
  • cluster/kube/gateway/httproute_test.go

Comment thread cluster/kube/client_gateway_test.go
@cloud-j-luna cloud-j-luna changed the title feat: ngf http options via snippets feat: ngf http options via snippet filters Aug 6, 2026
@cloud-j-luna
cloud-j-luna force-pushed the feat/ngf-http-options-via-snippets branch from 221cc8c to 60cf273 Compare August 6, 2026 19:19
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@cloud-j-luna cloud-j-luna reopened this Aug 7, 2026
Comment thread cluster/kube/gateway/httproute.go
@cloud-j-luna
cloud-j-luna force-pushed the feat/ngf-http-options-via-snippets branch from 60cf273 to 230d236 Compare August 7, 2026 15:27
@cloud-j-luna
cloud-j-luna requested a review from chalabi2 August 10, 2026 09:33

@chalabi2 chalabi2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One additional blocking proof gap: the green E2E check does not run TestGatewayAPISuite. The integration target explicitly uses -run TestIntegrationTestSuite, and the GitHub Actions log shows that exact command. The new Gateway API test therefore compiles but never executes.

Please add a Gateway/NGF CI job that runs TestGatewayAPISuite and verifies SnippetsFilter Accepted=True, HTTPRoute ResolvedRefs=True, Gateway/data-plane programming, and at least one real HTTP option such as the body-size limit.

Comment thread cluster/kube/gateway/httproute.go Outdated
Comment thread cluster/kube/gateway/nginx_gateway.go
…eady

Create the placeholder from an empty HTTPRouteSpec (no parentRefs, no
routable rules) instead of stripping only the filters. If the
SnippetsFilter apply or the acceptance wait fails, the route is never
attached to the Gateway, so it cannot serve traffic without its
http_options. Adds a new-route regression test asserting the placeholder
has no parentRefs and no rules on that failure path.
The value is inserted verbatim into a per-route SnippetsFilter, and NGF
marks a structurally valid filter Accepted=True without parsing its nginx
contents, so a bad value such as "16kb" would only surface later at
nginx -t and block the data plane. Reject anything that is not empty or a
valid positive nginx size (16k, 512k, 1m) at startup for both the provider
run and hostname operator commands.
@chalabi2
chalabi2 self-requested a review August 12, 2026 15:26

@chalabi2 chalabi2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

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.

2 participants