diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index c793f31b61..cf3aa8f551 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -210,7 +210,38 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res "Route is accepted", ) } + + // Warn about dots in the HTTPRoute name or rule names. Envoy derives Prometheus + // stat labels by splitting cluster names on ".", so a dotted name is truncated at + // the first dot and can collide with another route into duplicate metric series. + // This is set after the Accepted condition so it doesn't suppress it (the block + // above only fires when no other condition is present). + // See https://github.com/envoyproxy/gateway/issues/9576. + if dotted := dottedRouteNames(httpRoute); len(dotted) > 0 { + status.SetRouteStatusCondition(GetRouteStatus(httpRoute), + parentRef.routeParentStatusIdx, + httpRoute.GetGeneration(), + status.RouteConditionWarning, + metav1.ConditionTrue, + status.RouteReasonDottedName, + fmt.Sprintf("A dot in the HTTPRoute name or rule name(s) %v causes Envoy to truncate Prometheus metric labels at the first dot, which can produce duplicate metric series; avoid dots in HTTPRoute and rule names.", dotted), + ) + } + } +} + +// dottedRouteNames returns the HTTPRoute name and any rule names that contain a dot. +func dottedRouteNames(httpRoute *HTTPRouteContext) []string { + var names []string + if strings.Contains(httpRoute.GetName(), ".") { + names = append(names, httpRoute.GetName()) + } + for _, rule := range httpRoute.Spec.Rules { + if rule.Name != nil && strings.Contains(string(*rule.Name), ".") { + names = append(names, string(*rule.Name)) + } } + return names } func formatDroppedRuleMessage(unacceptedRules []int, err status.Error) string { diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 57b712fb8e..c1a8f9d28f 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -33,6 +33,15 @@ const ( // Network configuration related condition types RouteConditionBackendsAvailable gwapiv1.RouteConditionType = "BackendsAvailable" + + // RouteConditionWarning indicates that the route configuration contains + // non-critical issues that are accepted but require attention. + RouteConditionWarning gwapiv1.RouteConditionType = "Warning" + + // RouteReasonDottedName is used with the "Warning" condition when the route + // name or a rule name contains a dot, which causes Envoy to truncate Prometheus + // metric labels at the first dot and can produce duplicate metric series. + RouteReasonDottedName gwapiv1.RouteConditionReason = "DottedName" ) // Listener condition reasons for various error scenarios diff --git a/internal/gatewayapi/testdata/httproute-with-dot-in-name.in.yaml b/internal/gatewayapi/testdata/httproute-with-dot-in-name.in.yaml new file mode 100644 index 0000000000..0c432a4cc3 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-dot-in-name.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: echo.a.example.com + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-with-dot-in-name.out.yaml b/internal/gatewayapi/testdata/httproute-with-dot-in-name.out.yaml new file mode 100644 index 0000000000..b9cc982b60 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-dot-in-name.out.yaml @@ -0,0 +1,177 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: echo.a.example.com + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: A dot in the HTTPRoute name or rule name(s) [echo.a.example.com] + causes Envoy to truncate Prometheus metric labels at the first dot, which + can produce duplicate metric series; avoid dots in HTTPRoute and rule names. + reason: DottedName + status: "True" + type: Warning + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: echo.a.example.com + namespace: default + name: httproute/default/echo.a.example.com/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/echo.a.example.com/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: echo.a.example.com + namespace: default + name: httproute/default/echo.a.example.com/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/release-notes/current/new_features/9576-httproute-dotted-name-warning.md b/release-notes/current/new_features/9576-httproute-dotted-name-warning.md new file mode 100644 index 0000000000..a96e622ec6 --- /dev/null +++ b/release-notes/current/new_features/9576-httproute-dotted-name-warning.md @@ -0,0 +1 @@ +Envoy Gateway now sets a `Warning` status condition (reason `DottedName`) on an HTTPRoute whose name or a rule name contains a dot. Envoy derives Prometheus stat labels by splitting cluster names on `.`, so a dotted name is truncated at the first dot and can collide with another route into duplicate metric series. The condition signals users to avoid dots in HTTPRoute and rule names.