fix(helm): route Gateway API traffic to the chart service (#8145)

Co-authored-by: Hauser <housemecn@gmail.com>
This commit is contained in:
Chris
2026-09-28 02:26:56 +00:00
committed by GitHub
co-authored by Hauser
parent 2e014d25b3
commit afb9ddabb4
5 changed files with 120 additions and 41 deletions
+17 -2
View File
@@ -277,7 +277,8 @@ uer. `ClusterIssuer` or `Issuer`. |
| gatewayApi.listeners.tls.name | string | `tls` | Gateway API TLS passthrough listener name. |
| gatewayApi.listeners.tls.port | int | `443` | Gateway API TLS passthrough listener port. |
| gatewayApi.listeners.tls.backendPort | int | `null` | Backend service port that terminates TLS; defaults to the console port. |
| gatewayApi.hostname | string | Hostname to access RustFS via gateway api. |
| gatewayApi.hostname | string | `example.rustfs.com` | Console hostname for Gateway API. |
| gatewayApi.endpointHostname | string | `""` | Optional separate S3 endpoint hostname; empty disables the S3 HTTPRoute. |
| gatewayApi.secretName | string | Secret tls to via RustFS using HTTPS. |
| gatewayApi.existingGateway.name | string | `""` | The existing gateway name, instead of creating a new one. |
| gatewayApi.existingGateway.namespace | string | `""` | The namespace of the existing gateway, if not the local namespace. |
@@ -449,7 +450,21 @@ NAME HOSTNAMES AGE
rustfs-route ["example.rustfs.com"] 172m
```
Then, via RustFS instance via `https://example.rustfs.com` or `http://example.rustfs.com`.
Access the console at `https://example.rustfs.com`; HTTP redirects to HTTPS by default. HTTPRoutes reference the chart's Kubernetes Service directly and do not require the TraefikService CRD or Traefik's Kubernetes CRD provider. The former TraefikService cookie stickiness is no longer configured by this chart.
To expose the S3 endpoint alongside the console, set a different `gatewayApi.endpointHostname`:
```yaml
gatewayApi:
enabled: true
hostname: console.example.com
endpointHostname: s3.example.com
existingGateway:
name: shared-gateway
namespace: gateway-system
```
The console route uses `service.console.port` (9001 by default), and the optional S3 route uses `service.endpoint.port` (9000 by default). Both attach to the configured HTTPS listener and preserve request paths. Configure DNS and the Gateway's TLS certificate for both hostnames. When using a Gateway in another namespace, its listener must allow routes from the RustFS namespace. Leaving `endpointHostname` empty preserves the console-only behavior.
For end-to-end encryption, set `gatewayApi.listeners.tls.enabled` to `true`. The chart then adds a `TLS` listener with `tls.mode: Passthrough` to the `Gateway` and generates a `TLSRoute` that forwards the encrypted stream to the RustFS service, where TLS is terminated on the backend side. Note that backend TLS termination must be configured on RustFS itself (for example `RUSTFS_TLS_PATH` pointing to server certificates), and the installed Gateway API CRDs must include `TLSRoute`.
+23 -18
View File
@@ -1,42 +1,47 @@
{{- if .Values.gatewayApi.enabled }}
{{- $routes := list (dict "name" "route" "hostname" .Values.gatewayApi.hostname "port" .Values.service.console.port) }}
{{- with .Values.gatewayApi.endpointHostname }}
{{- if eq . $.Values.gatewayApi.hostname }}
{{- fail "gatewayApi.endpointHostname must differ from gatewayApi.hostname" }}
{{- end }}
{{- $routes = append $routes (dict "name" "endpoint-route" "hostname" . "port" $.Values.service.endpoint.port) }}
{{- end }}
{{- range $routes }}
{{- if eq .name "endpoint-route" }}
---
{{- end }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ include "rustfs.fullname" . }}-route
namespace: {{ .Release.Namespace }}
name: {{ include "rustfs.fullname" $ }}-{{ .name }}
namespace: {{ $.Release.Namespace }}
annotations:
{{- if eq .Values.gatewayApi.gatewayClass "contour" }}
{{- if and (eq $.Values.gatewayApi.gatewayClass "contour") (eq .name "route") }}
projectcontour.io/upstream-hash-policy: "cookie"
{{- end }}
spec:
parentRefs:
{{- if .Values.gatewayApi.existingGateway.name }}
- name: {{ .Values.gatewayApi.existingGateway.name }}
{{- if .Values.gatewayApi.existingGateway.namespace }}
namespace: {{ .Values.gatewayApi.existingGateway.namespace }}
{{- if $.Values.gatewayApi.existingGateway.name }}
- name: {{ $.Values.gatewayApi.existingGateway.name }}
{{- if $.Values.gatewayApi.existingGateway.namespace }}
namespace: {{ $.Values.gatewayApi.existingGateway.namespace }}
{{- end }}
sectionName: {{ .Values.gatewayApi.listeners.https.name | default "websecure"}}
sectionName: {{ $.Values.gatewayApi.listeners.https.name | default "websecure"}}
{{- else }}
- name: {{ include "rustfs.fullname" $ }}-gateway
sectionName: {{ .Values.gatewayApi.listeners.https.name | default "websecure"}}
sectionName: {{ $.Values.gatewayApi.listeners.https.name | default "websecure"}}
{{- end }}
hostnames:
- {{ .Values.gatewayApi.hostname }}
- {{ .hostname | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
{{- if eq .Values.gatewayApi.gatewayClass "traefik" }}
- name: {{ include "rustfs.fullname" . }}-sticky-svc
port: {{ .Values.service.console.port }}
kind: TraefikService
group: traefik.io
{{- else }}
- name: {{ include "rustfs.fullname" $ }}-svc
port: {{ .Values.service.console.port }}
{{- end }}
port: {{ .port }}
{{- end }}
{{- if .Values.gatewayApi.httpToHttpsRedirect }}
---
apiVersion: gateway.networking.k8s.io/v1
@@ -1,20 +0,0 @@
{{- if and .Values.gatewayApi.enabled (eq .Values.gatewayApi.gatewayClass "traefik") }}
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: {{ include "rustfs.fullname" . }}-sticky-svc
namespace: {{ .Release.Namespace }}
spec:
weighted:
services:
- name: {{ include "rustfs.fullname" . }}-svc
namespace: {{ .Release.Namespace }}
port: {{ .Values.service.console.port }}
weight: 1
sticky:
cookie:
name: rustfs
httpOnly: true
secure: {{ .Values.ingress.tls.enabled | default false }}
sameSite: {{ if .Values.ingress.tls.enabled }}none{{ else }}lax{{ end }}
{{- end }}
+2 -1
View File
@@ -381,7 +381,8 @@ gatewayApi:
port: 443
# Service port that terminates TLS on the backend; defaults to the console port.
backendPort: null
hostname: example.rustfs.com
hostname: example.rustfs.com # Console hostname.
endpointHostname: "" # Optional separate hostname for the S3 endpoint.
httpToHttpsRedirect: true
existingGateway:
name: ""
+78
View File
@@ -73,6 +73,84 @@ render_server_cert() {
'
}
# Gateway routes must resolve to the rendered Kubernetes Service without Traefik CRDs.
for gateway_class in traefik contour istio; do
for distributed in false true; do
standalone=true
if [[ "$distributed" == true ]]; then standalone=false; fi
gateway_output=$(render_chart \
--set "mode.distributed.enabled=$distributed" \
--set "mode.standalone.enabled=$standalone" \
--set gatewayApi.enabled=true \
--set "gatewayApi.gatewayClass=$gateway_class" \
--set fullnameOverride=gateway-test \
--set service.console.port=19001 \
--set service.endpoint.port=19000 \
--set gatewayApi.hostname=console.example.com \
--set gatewayApi.endpointHostname=s3.example.com \
--set gatewayApi.existingGateway.name=shared-gateway \
--set gatewayApi.existingGateway.namespace=gateway-system \
--set gatewayApi.listeners.https.name=https)
yq eval -e 'select(.kind == "Service" and .metadata.name == "gateway-test-svc") |
.metadata.namespace == "rustfs" and
.spec.ports[0].port == 19000 and .spec.ports[1].port == 19001' - <<<"$gateway_output" >/dev/null
for route in route endpoint-route; do
expected_port=19001
expected_host=console.example.com
if [[ "$route" == endpoint-route ]]; then
expected_port=19000
expected_host=s3.example.com
fi
ROUTE_NAME="gateway-test-$route" EXPECTED_PORT="$expected_port" EXPECTED_HOST="$expected_host" \
yq eval -e 'select(.kind == "HTTPRoute" and .metadata.name == strenv(ROUTE_NAME)) |
.metadata.namespace == "rustfs" and
.spec.hostnames[0] == strenv(EXPECTED_HOST) and
.spec.parentRefs[0].name == "shared-gateway" and
.spec.parentRefs[0].namespace == "gateway-system" and
.spec.parentRefs[0].sectionName == "https" and
.spec.rules[0].matches[0].path.type == "PathPrefix" and
.spec.rules[0].matches[0].path.value == "/" and
(.spec.rules[0].backendRefs | length) == 1 and
.spec.rules[0].backendRefs[0].name == "gateway-test-svc" and
.spec.rules[0].backendRefs[0].port == env(EXPECTED_PORT) and
(.spec.rules[0].backendRefs[0].kind // "Service") == "Service" and
(.spec.rules[0].backendRefs[0].group // "") == ""' - <<<"$gateway_output" >/dev/null
done
if grep -q 'kind: TraefikService' <<<"$gateway_output"; then
echo "Gateway API must not require a TraefikService CRD" >&2
exit 1
fi
done
done
gateway_default=$(render_chart --set gatewayApi.enabled=true)
yq eval -e 'select(.kind == "HTTPRoute" and .metadata.name == "rustfs-route") |
.spec.rules[0].backendRefs[0].name == "rustfs-svc" and
.spec.rules[0].backendRefs[0].port == 9001 and
.spec.parentRefs[0].name == "rustfs-gateway" and
.spec.parentRefs[0].sectionName == "websecure"' - <<<"$gateway_default" >/dev/null
if grep -q 'name: rustfs-endpoint-route' <<<"$gateway_default"; then
echo "The S3 route must be opt-in" >&2
exit 1
fi
gateway_no_redirect=$(render_chart --set gatewayApi.enabled=true --set gatewayApi.endpointHostname=s3.example.com --set gatewayApi.httpToHttpsRedirect=false)
if grep -q 'type: RequestRedirect' <<<"$gateway_no_redirect"; then
echo "Disabling HTTPS redirects must suppress the redirect route" >&2
exit 1
fi
if render_chart --set gatewayApi.enabled=true --set gatewayApi.endpointHostname=example.rustfs.com >/dev/null 2>&1; then
echo "Console and S3 routes must not share a hostname" >&2
exit 1
fi
gateway_disabled=$(render_chart --set gatewayApi.endpointHostname=s3.example.com)
if grep -q 'kind: HTTPRoute' <<<"$gateway_disabled"; then
echo "Disabling Gateway API must suppress all HTTP routes" >&2
exit 1
fi
recreate_output=$(render_standalone_deployment --set mode.standalone.strategy.type=Recreate)
grep -q "type: Recreate" <<<"$recreate_output"
if grep -q "rollingUpdate:" <<<"$recreate_output"; then