Skip to content

feat: add per-route stat prefix support to TrafficPolicy#14409

Merged
davidjumani merged 4 commits into
kgateway-dev:mainfrom
puertomontt:feat/per-route-stat-prefix
Jul 15, 2026
Merged

feat: add per-route stat prefix support to TrafficPolicy#14409
davidjumani merged 4 commits into
kgateway-dev:mainfrom
puertomontt:feat/per-route-stat-prefix

Conversation

@puertomontt

@puertomontt puertomontt commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Description

Implements #13564: adds an opt-in, per-route statistics capability to TrafficPolicy.

Operators can now set a custom prefix on the Envoy route so that per-route stats are emitted, giving a clear connection between Kubernetes resources and Envoy metrics. When set, Envoy emits stats under vhost.<vhost>.route.<statPrefix>.*.

What changed

  • New optional field spec.statPrefix on TrafficPolicy. Opt-in: unset means no per-route stat prefix is configured (no extra metrics cardinality).
  • The value supports template variables resolved at translation time from the targeted route's metadata:
    • {{route_name}} – the route resource name
    • {{route_namespace}} – the route resource namespace
    • {{rule_name}} – the matched route rule name (empty if unnamed)
  • The prefix is set on the Envoy Route (not the RouteAction), so it also applies to redirect/direct-response routes.
  • The field is only honored for HTTPRoute and GRPCRoute targets, and a spec-level CEL rule rejects it on any other target (e.g. a Gateway) at admission.

Validation

  • The syntax is enforced entirely by the CRD schema. The Pattern only admits stat-safe literal runs ([a-zA-Z0-9_%.-]) and well-formed {{ ... }} tokens referencing a supported variable, so stray/unmatched braces, unsupported variable names, and unsafe characters are rejected at admission. MaxLength is 256.
  • A spec-level XValidation (CEL) rule restricts the field to HTTPRoute/GRPCRoute targets.
  • Empty .-separated segments left by an empty variable (e.g. an unnamed rule) are dropped when resolving, so no doubled/trailing dots leak into stat names.

Notes for reviewers

  • A RuleName field was added to ir.HttpRouteRuleMatchIR to carry the user-authored Gateway API rule.name (the existing Name is a generated, per-match unique name). GRPCRoute rules convert into the same IR, so {{rule_name}} works for both.
  • The CRD ships inside a Helm chart whose templates/ dir is rendered as Go templates, so codegen (hack/generate.sh) escapes the literal {{ }} tokens in the generated CRD description as Helm string literals; they render back to the original text (verified via helm lint/helm template). The Pattern uses escaped braces (\{\{) so it is left untouched.

Change Type

/kind feature

Changelog

Added an opt-in `statPrefix` field to `TrafficPolicy` that sets the Envoy route `stat_prefix`, enabling per-route statistics. The value supports `{{route_name}}`, `{{route_namespace}}`, and `{{rule_name}}` template variables and is only honored for HTTPRoute and GRPCRoute targets.

@puertomontt
puertomontt requested a review from a team as a code owner July 14, 2026 16:51
Copilot AI review requested due to automatic review settings July 14, 2026 16:51
@puertomontt
puertomontt force-pushed the feat/per-route-stat-prefix branch from 59fd7f3 to 73a6975 Compare July 14, 2026 16:57
@puertomontt
puertomontt force-pushed the feat/per-route-stat-prefix branch from 73a6975 to 91491ba Compare July 14, 2026 17:09

Copilot AI 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.

Pull request overview

Adds opt-in per-route Envoy Route.stat_prefix support to the TrafficPolicy plugin, allowing operators to template route metric prefixes using targeted route metadata (route namespace/name and rule name) during translation.

Changes:

  • Introduces spec.statPrefix on TrafficPolicy with CRD validation and documentation.
  • Extends route IR to carry user-authored rule name (RuleName) and wires it through HTTPRoute translation for templating.
  • Implements TrafficPolicy IR + merge + translation-pass application of route stat_prefix, with unit and golden translation tests.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/pluginsdk/ir/gw2.go Adds RuleName to route match IR for user-facing templating.
pkg/kgateway/translator/httproute/gateway_http_route_translator.go Populates RuleName from the rule’s authored name during route translation.
pkg/kgateway/translator/gateway/testutils/outputs/traffic-policy/stat-prefix.yaml Adds golden output verifying Envoy routes get statPrefix set.
pkg/kgateway/translator/gateway/testutils/inputs/traffic-policy/stat-prefix.yaml Adds input manifests covering templated and literal statPrefix behavior.
pkg/kgateway/translator/gateway/gateway_translator_test.go Registers a new gateway translator test case for statPrefix.
pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go Includes statPrefix in policy IR equality/validation and applies it per-route.
pkg/kgateway/extensions2/plugins/trafficpolicy/stat_prefix.go Implements IR, validation, template substitution, and Envoy Route statPrefix application.
pkg/kgateway/extensions2/plugins/trafficpolicy/stat_prefix_test.go Adds unit tests for validation, IR construction, and per-route application behavior.
pkg/kgateway/extensions2/plugins/trafficpolicy/merge.go Adds merge support for statPrefix across attached TrafficPolicies.
pkg/kgateway/extensions2/plugins/trafficpolicy/constructor.go Constructs statPrefix sub-IR during TrafficPolicy IR build.
install/helm/kgateway-crds/templates/gateway.kgateway.dev_trafficpolicies.yaml Updates generated CRD schema to include statPrefix.
api/v1alpha1/kgateway/zz_generated.deepcopy.go Updates deepcopy for the new StatPrefix field.
api/v1alpha1/kgateway/traffic_policy_types.go Adds the StatPrefix field, docs, and kubebuilder validations.
Files not reviewed (1)
  • api/v1alpha1/kgateway/zz_generated.deepcopy.go: Generated file

Comment thread pkg/kgateway/extensions2/plugins/trafficpolicy/stat_prefix.go Outdated
Comment on lines +120 to +123
// A template such as `{{rule_name}}` can resolve to an empty string when the
// rule is unnamed; trim any leading/trailing separators left behind so the
// emitted stat name stays clean.
out.StatPrefix = strings.Trim(out.GetStatPrefix(), ".")
Comment thread api/v1alpha1/kgateway/traffic_policy_types.go Outdated
@puertomontt
puertomontt force-pushed the feat/per-route-stat-prefix branch 4 times, most recently from 6954c28 to 0d8b0e4 Compare July 14, 2026 17:42
@puertomontt
puertomontt requested a review from Copilot July 14, 2026 17:43

Copilot AI 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.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • api/v1alpha1/kgateway/zz_generated.deepcopy.go: Generated file

Comment thread pkg/kgateway/extensions2/plugins/trafficpolicy/stat_prefix.go

Copilot AI 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.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • api/v1alpha1/kgateway/zz_generated.deepcopy.go: Generated file

Comment on lines +97 to +103
// A template variable (e.g. rule_name for an unnamed rule) can resolve to an
// empty string. '.' delimits segments in Envoy stat names, so drop any empty
// '.'-separated segments this leaves behind (leading, trailing, or in the
// middle) to avoid doubled or trailing dots, which produce malformed stat
// names and blank levels in dot-hierarchy sinks such as statsd/Graphite.
out.StatPrefix = joinNonEmptyStatSegments(resolved)
}
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9_%.-]|\{\{\s*(route_name|route_namespace|rule_name)\s*\}\})+$`
StatPrefix *string `json:"statPrefix,omitempty"`

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.

Should this have a default such as namespace.name.rule ? Would that introduce a breaking change ?

@davidjumani
davidjumani enabled auto-merge July 14, 2026 19:13
@puertomontt
puertomontt force-pushed the feat/per-route-stat-prefix branch 2 times, most recently from 52caf96 to 1a3c286 Compare July 14, 2026 19:20
Add an opt-in `spec.statPrefix` field on TrafficPolicy that sets Envoy's
route-level `stat_prefix`, enabling per-route statistics that map back to
Kubernetes resources.

The value is composed of stat-safe literal characters and/or `{{ ... }}`
template tokens (`{{route_name}}`, `{{route_namespace}}`, `{{rule_name}}`)
that are resolved at translation time from the targeted route's metadata. The
prefix is set on the Envoy Route (not the RouteAction), so it also applies to
redirect and direct-response routes.

Validation is enforced entirely by the CRD schema: the field's Pattern only
admits stat-safe literal runs and well-formed template tokens referencing a
supported variable name, and a spec-level CEL rule restricts the field to
HTTPRoute and GRPCRoute targets (it is not honored on Gateway attachments).

Because the CRD ships inside a Helm chart whose templates dir is rendered as
Go templates, codegen escapes the literal `{{ }}` tokens in the generated CRD
description so they survive `helm lint` and render back to the original text.
The Pattern uses escaped braces (`\{\{`) so it is left untouched.

Fixes kgateway-dev#13564

Signed-off-by: omar <[email protected]>
Co-authored-by: Mihir Dixit <[email protected]>
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
@davidjumani
davidjumani removed this pull request from the merge queue due to a manual request Jul 15, 2026
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 15, 2026
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 15, 2026
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 15, 2026
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 15, 2026
@davidjumani
davidjumani enabled auto-merge July 15, 2026 19:01
@davidjumani
davidjumani added this pull request to the merge queue Jul 15, 2026
Merged via the queue into kgateway-dev:main with commit 68c47f0 Jul 15, 2026
32 checks passed
@puertomontt puertomontt linked an issue Jul 15, 2026 that may be closed by this pull request
sheidkamp pushed a commit to sheidkamp/kgateway that referenced this pull request Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature. release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add per-route statistics/metrics support

5 participants