From e088fcc4f02909656fac0690bdad1d0fc1a5ef0f Mon Sep 17 00:00:00 2001 From: Andreas Beuge Date: Tue, 19 May 2026 16:43:58 +0200 Subject: [PATCH] feat: add envoy grpc route support --- parcellab/common/Chart.yaml | 2 +- .../templates/_backendtrafficpolicy.tpl | 14 ++-- parcellab/common/templates/_grpcroutes.tpl | 78 +++++++++++++++++++ parcellab/common/templates/_httproutes.tpl | 9 +++ parcellab/common/values.yaml | 1 + parcellab/microservice/Chart.yaml | 2 +- parcellab/microservice/README.md | 2 +- .../microservice/templates/grpcroutes.yaml | 1 + parcellab/microservice/values.yaml | 35 ++++++++- parcellab/monolith/Chart.yaml | 2 +- parcellab/monolith/README.md | 2 +- parcellab/monolith/templates/grpcroutes.yaml | 1 + parcellab/monolith/values.yaml | 35 ++++++++- 13 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 parcellab/common/templates/_grpcroutes.tpl create mode 100644 parcellab/microservice/templates/grpcroutes.yaml create mode 100644 parcellab/monolith/templates/grpcroutes.yaml diff --git a/parcellab/common/Chart.yaml b/parcellab/common/Chart.yaml index 1d4af6c..4226593 100644 --- a/parcellab/common/Chart.yaml +++ b/parcellab/common/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: common description: A Helm chart library for parcelLab charts type: library -version: 1.3.7 +version: 1.3.8 maintainers: - name: parcelLab email: engineering@parcellab.com diff --git a/parcellab/common/templates/_backendtrafficpolicy.tpl b/parcellab/common/templates/_backendtrafficpolicy.tpl index 94819c2..20d6905 100644 --- a/parcellab/common/templates/_backendtrafficpolicy.tpl +++ b/parcellab/common/templates/_backendtrafficpolicy.tpl @@ -5,9 +5,11 @@ dict "Values" "the values scope" "Release" .Release - "route" "the current HTTPRoute object (optional)" - "index" "the httpRoutes index (optional)" - "routeName" "the rendered HTTPRoute name (optional)" + "route" "the current Gateway API route object (optional)" + "index" "the route values index (optional)" + "routeName" "the rendered route name (optional)" + "routeKind" "the Gateway API route kind (optional, default HTTPRoute)" + "routeValuesPath" "the values path for route errors (optional, default envoy.httpRoutes)" "globalLabels" "common labels (optional)" ) }} */}} @@ -16,6 +18,8 @@ {{- $route := .route | default dict -}} {{- $index := .index | default 0 -}} {{- $routeName := .routeName | default "" -}} +{{- $routeKind := .routeKind | default "HTTPRoute" -}} +{{- $routeValuesPath := .routeValuesPath | default "envoy.httpRoutes" -}} {{- $globalLabels := .globalLabels | default (include "common.labels" .) -}} {{- $serviceNamespace := .Release.Namespace -}} {{- $envoy := .Values.envoy | default dict -}} @@ -78,7 +82,7 @@ {{- end -}} {{- if and (eq (len $btpSpec) 0) (not $btpHasTargetRef) (eq (len $btpTargetRefs) 0) (eq (len $btpTargetSelectors) 0) (not $btpSpecHasTargetRef) (not $btpSpecHasTargetRefs) (not $btpSpecHasTargetSelectors) -}} {{- if $hasRoutePolicy -}} -{{- fail (printf "envoy.httpRoutes[%d].backendTrafficPolicy requires spec or fields" $index) -}} +{{- fail (printf "%s[%d].backendTrafficPolicy requires spec or fields" $routeValuesPath $index) -}} {{- else -}} {{- fail "envoy.backendTrafficPolicy requires spec or fields" -}} {{- end -}} @@ -114,7 +118,7 @@ spec: {{- else if and $hasRoutePolicy (not $btpSpecHasTargetRef) (not $btpSpecHasTargetRefs) (not $btpSpecHasTargetSelectors) }} targetRefs: - group: gateway.networking.k8s.io - kind: HTTPRoute + kind: {{ $routeKind }} name: {{ $routeName }} {{- end }} {{- if gt (len $btpSpec) 0 }} diff --git a/parcellab/common/templates/_grpcroutes.tpl b/parcellab/common/templates/_grpcroutes.tpl new file mode 100644 index 0000000..be4133a --- /dev/null +++ b/parcellab/common/templates/_grpcroutes.tpl @@ -0,0 +1,78 @@ +{{/* vim: set filetype=mustache: */}} +{{/* + Common GRPCRoute definition with deterministic names and labels: + {{ include "common.grpcroutes" . }} +*/}} + +{{- define "common.grpcroutes" -}} +{{- $root := . -}} +{{- $envoy := .Values.envoy | default dict -}} +{{- if $envoy.enabled -}} +{{- $gateway := default (dict "name" "gateway-api" "namespace" "envoy-gateway") $envoy.gateway -}} +{{- $grpcroutes := default (list) $envoy.grpcRoutes -}} +{{- $baseName := include "common.fullname" . -}} +{{- $globalLabels := include "common.labels" . -}} +{{- $serviceNamespace := .Release.Namespace -}} +{{- $security := default dict $envoy.security -}} +{{- $securityEnabled := default false $security.enabled -}} +{{- $securityLabelKey := printf "%s/security-required" (include "common.parcellabtagsdomain" .) -}} +{{- $rolloutServices := include "common.rolloutServicesMap" (dict "root" $root "baseName" $baseName) | fromJson -}} + +{{- range $index, $route := $grpcroutes }} +{{- $hosts := required (printf "envoy.grpcRoutes[%d].hosts is required" $index) $route.hosts -}} +{{- if eq (len $hosts) 0 -}} +{{- fail (printf "envoy.grpcRoutes[%d].hosts cannot be empty" $index) -}} +{{- end -}} +{{- $policyRoute := $route -}} +{{- $rawRouteName := default (printf "%s-grpc-%d" $baseName $index) $route.name -}} +{{- $sanitizedRouteName := trunc 63 (trimSuffix "-" (regexReplaceAll "[^a-z0-9-]" (lower $rawRouteName) "-")) -}} +{{- $routeName := default (printf "%s-grpc-%d" $baseName $index) $sanitizedRouteName }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GRPCRoute +metadata: + name: {{ $routeName }} + namespace: {{ $serviceNamespace }} + labels: + {{- $globalLabels | nindent 4 }} + {{ $securityLabelKey }}: {{ (ternary "true" "false" $securityEnabled) | quote }} + {{- with $route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + external-dns.alpha.kubernetes.io/hostname: "{{ join "," $route.hosts }}" +spec: + parentRefs: + - name: {{ $gateway.name }} + namespace: {{ $gateway.namespace }} + group: gateway.networking.k8s.io + kind: Gateway + hostnames: + {{- range $hosts }} + - {{ . | quote }} + {{- end }} + {{- with $route.rules }} + rules: + {{- range $rule := . }} + {{- $ruleCopy := deepCopy $rule -}} + {{- if $ruleCopy.backendRefs }} + {{- range $backend := $ruleCopy.backendRefs }} + {{- $backendKind := default "Service" $backend.kind -}} + {{- $backendGroup := default "" $backend.group -}} + {{- if and (eq $backendKind "Service") (eq $backendGroup "") }} + {{- $backendName := $backend.name -}} + {{- if and $backendName (hasKey $rolloutServices $backendName) (not (hasSuffix "-rollout" $backendName)) -}} + {{- $_ := set $backend "name" (printf "%s-rollout" $backendName) -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- toYaml (list $ruleCopy) | nindent 4 }} + {{- end }} + {{- end }} +{{- if hasKey $route "backendTrafficPolicy" }} +{{ include "common.backendtrafficpolicy" (dict "Values" $root.Values "Release" $root.Release "Chart" $root.Chart "route" $policyRoute "index" $index "routeName" $routeName "routeKind" "GRPCRoute" "routeValuesPath" "envoy.grpcRoutes" "globalLabels" $globalLabels) }} +{{- end }} +{{ end }} +{{- end }} +{{- end }} diff --git a/parcellab/common/templates/_httproutes.tpl b/parcellab/common/templates/_httproutes.tpl index 0fbb4c8..b1bdb90 100644 --- a/parcellab/common/templates/_httproutes.tpl +++ b/parcellab/common/templates/_httproutes.tpl @@ -10,6 +10,7 @@ {{- if $envoy.enabled -}} {{- $gateway := default (dict "name" "gateway-api" "namespace" "envoy-gateway") $envoy.gateway -}} {{- $httproutes := default (list) $envoy.httpRoutes -}} +{{- $grpcroutes := default (list) $envoy.grpcRoutes -}} {{- $globalBackendTrafficPolicy := $envoy.backendTrafficPolicy | default dict -}} {{- /* ClientTrafficPolicy removed: it can only target Gateway, so it belongs in the gateway chart */ -}} {{- $baseName := include "common.fullname" . -}} @@ -34,6 +35,14 @@ {{- $globalBackendTrafficPolicyTargetRefs = append $globalBackendTrafficPolicyTargetRefs (dict "group" "gateway.networking.k8s.io" "kind" "HTTPRoute" "name" $routeName) -}} {{- end -}} {{- end -}} +{{- range $index, $route := $grpcroutes }} +{{- if not (hasKey $route "backendTrafficPolicy") -}} +{{- $rawRouteName := default (printf "%s-grpc-%d" $baseName $index) $route.name -}} +{{- $sanitizedRouteName := trunc 63 (trimSuffix "-" (regexReplaceAll "[^a-z0-9-]" (lower $rawRouteName) "-")) -}} +{{- $routeName := default (printf "%s-grpc-%d" $baseName $index) $sanitizedRouteName -}} +{{- $globalBackendTrafficPolicyTargetRefs = append $globalBackendTrafficPolicyTargetRefs (dict "group" "gateway.networking.k8s.io" "kind" "GRPCRoute" "name" $routeName) -}} +{{- end -}} +{{- end -}} {{- end -}} {{- if and $globalBackendTrafficPolicyEnabled (or $globalBackendTrafficPolicyHasTargetRef $globalBackendTrafficPolicyHasTargetRefs $globalBackendTrafficPolicyHasTargetSelectors (gt (len $globalBackendTrafficPolicyTargetRefs) 0)) -}} {{- $globalBackendPolicy := deepCopy $globalBackendTrafficPolicy -}} diff --git a/parcellab/common/values.yaml b/parcellab/common/values.yaml index d5e7f67..8f64b0c 100644 --- a/parcellab/common/values.yaml +++ b/parcellab/common/values.yaml @@ -29,6 +29,7 @@ envoy: gateway: {} referenceGrant: {} httpRoutes: [] + grpcRoutes: [] name: common terminationGracePeriodSeconds: 30 diff --git a/parcellab/microservice/Chart.yaml b/parcellab/microservice/Chart.yaml index f120b7b..a584350 100644 --- a/parcellab/microservice/Chart.yaml +++ b/parcellab/microservice/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: microservice description: Simple microservice -version: 0.5.7 +version: 0.5.8 dependencies: - name: common version: "*" diff --git a/parcellab/microservice/README.md b/parcellab/microservice/README.md index 71df0c1..5356281 100644 --- a/parcellab/microservice/README.md +++ b/parcellab/microservice/README.md @@ -32,7 +32,7 @@ needs. - `hpa` - Horizontal automatic scaling rules of pods. Can be defined with the `autoscaling` setting. - `envoy` - - Envoy Gateway resources (HTTPRoute, ReferenceGrant, BackendTrafficPolicy). Defined under `envoy.*`. + - Envoy Gateway resources (HTTPRoute, GRPCRoute, ReferenceGrant, BackendTrafficPolicy). Defined under `envoy.*`. - `ingress` - Rules to open external access to the workload. Can be defined with `ingress`. - `poddisruptionbudget` diff --git a/parcellab/microservice/templates/grpcroutes.yaml b/parcellab/microservice/templates/grpcroutes.yaml new file mode 100644 index 0000000..fb5596b --- /dev/null +++ b/parcellab/microservice/templates/grpcroutes.yaml @@ -0,0 +1 @@ +{{- include "common.grpcroutes" . }} diff --git a/parcellab/microservice/values.yaml b/parcellab/microservice/values.yaml index 9eb3ed1..6463355 100644 --- a/parcellab/microservice/values.yaml +++ b/parcellab/microservice/values.yaml @@ -59,6 +59,8 @@ envoy: from: - group: gateway.networking.k8s.io kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute - group: gateway.networking.k8s.io kind: SecurityPolicy to: @@ -66,7 +68,7 @@ envoy: kind: Gateway name: gateway-api # backendTrafficPolicy: - # # Global policy for all HTTPRoutes. If a route defines its own + # # Global policy for all HTTPRoutes and GRPCRoutes. If a route defines its own # # backendTrafficPolicy, that route is excluded from the global policy. # # enabled: true # # targetSelectors: @@ -109,6 +111,37 @@ envoy: # # requestTimeout: 30s # labels: # foo: bar # optional + grpcRoutes: [] + # - name: my-default-grpc-route + # hosts: + # - my-app.example.com + # rules: + # - name: default-grpc-route + # matches: + # - method: + # service: my.package.Service + # method: GetItem + # backendRefs: + # - name: my-app + # port: 5000 + # group: "" + # kind: Service + # backendTrafficPolicy: + # # enabled: true + # # name: my-app-grpc-policy + # # labels: {} + # # annotations: {} + # # targetRefs: [] # default: the GRPCRoute above + # # loadBalancer: + # # type: LeastRequest + # # timeout: + # # tcp: + # # connectTimeout: 5s + # # http: + # # connectionIdleTimeout: 60s + # # requestTimeout: 30s + # labels: + # foo: bar # optional security: enabled: false # enabled: true diff --git a/parcellab/monolith/Chart.yaml b/parcellab/monolith/Chart.yaml index c30400c..a895ad7 100644 --- a/parcellab/monolith/Chart.yaml +++ b/parcellab/monolith/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: monolith description: Application that may define multiple services and cronjobs -version: 0.5.8 +version: 0.5.9 dependencies: - name: common version: "*" diff --git a/parcellab/monolith/README.md b/parcellab/monolith/README.md index bcd0006..1db0e1c 100644 --- a/parcellab/monolith/README.md +++ b/parcellab/monolith/README.md @@ -32,7 +32,7 @@ needs. - `hpa` - Horizontal automatic scaling rules of pods. Can be defined with the `autoscaling` setting. - `envoy` - - Envoy Gateway resources (HTTPRoute, ReferenceGrant, SecurityPolicy, BackendTrafficPolicy). Defined under `envoy.*`. + - Envoy Gateway resources (HTTPRoute, GRPCRoute, ReferenceGrant, SecurityPolicy, BackendTrafficPolicy). Defined under `envoy.*`. - `ingress` - Rules to open external access to the workload. Can be defined with `ingress`. - `poddisruptionbudget` diff --git a/parcellab/monolith/templates/grpcroutes.yaml b/parcellab/monolith/templates/grpcroutes.yaml new file mode 100644 index 0000000..fb5596b --- /dev/null +++ b/parcellab/monolith/templates/grpcroutes.yaml @@ -0,0 +1 @@ +{{- include "common.grpcroutes" . }} diff --git a/parcellab/monolith/values.yaml b/parcellab/monolith/values.yaml index 1f51c46..f60ec31 100644 --- a/parcellab/monolith/values.yaml +++ b/parcellab/monolith/values.yaml @@ -88,6 +88,8 @@ envoy: from: - group: gateway.networking.k8s.io kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute - group: gateway.networking.k8s.io kind: SecurityPolicy to: @@ -95,7 +97,7 @@ envoy: kind: Gateway name: gateway-api # backendTrafficPolicy: - # # Global policy for all HTTPRoutes. If a route defines its own + # # Global policy for all HTTPRoutes and GRPCRoutes. If a route defines its own # # backendTrafficPolicy, that route is excluded from the global policy. # # enabled: true # # targetSelectors: @@ -138,6 +140,37 @@ envoy: # # requestTimeout: 30s # labels: # foo: bar # optional + grpcRoutes: [] + # - name: my-default-grpc-route + # hosts: + # - my-app.example.com + # rules: + # - name: default-grpc-route + # matches: + # - method: + # service: my.package.Service + # method: GetItem + # backendRefs: + # - name: my-app + # port: 5000 + # group: "" + # kind: Service + # backendTrafficPolicy: + # # enabled: true + # # name: my-app-grpc-policy + # # labels: {} + # # annotations: {} + # # targetRefs: [] # default: the GRPCRoute above + # # loadBalancer: + # # type: LeastRequest + # # timeout: + # # tcp: + # # connectTimeout: 5s + # # http: + # # connectionIdleTimeout: 60s + # # requestTimeout: 30s + # labels: + # foo: bar # optional security: enabled: false # enabled: true