Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
project: charts/redpanda
kind: Added
body: |-
Added Gateway API TLSRoute support for external access. Individual external
listeners can opt into TLSRoute mode via `gateway: true`, enabling SNI-based
routing through a user-managed Gateway. This supports per-listener domains
and gradual migration from NodePort/LoadBalancer to Gateway API.
time: 2026-04-14T20:00:00.000000-04:00
19 changes: 19 additions & 0 deletions charts/redpanda/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
policyv1 "k8s.io/api/policy/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"

consolechart "github.com/redpanda-data/redpanda-operator/charts/console/v3/chart"
Expand Down Expand Up @@ -56,6 +57,7 @@ func Types() []kube.Object {
&corev1.Secret{},
&corev1.ServiceAccount{},
&corev1.Service{},
&TLSRoute{},
&monitoringv1.PodMonitor{},
&monitoringv1.ServiceMonitor{},
&networkingv1.Ingress{},
Expand All @@ -72,6 +74,15 @@ func init() {
must(scheme.AddToScheme(Scheme))
must(certmanagerv1.AddToScheme(Scheme))
must(monitoringv1.AddToScheme(Scheme))
addTLSRouteToScheme(Scheme)
}

// addTLSRouteToScheme registers our lightweight TLSRoute type with a
// runtime.Scheme so the test harness can decode it.
// +gotohelm:ignore=true
func addTLSRouteToScheme(s *runtime.Scheme) {
gv := schema.GroupVersion{Group: "gateway.networking.k8s.io", Version: "v1alpha2"}
s.AddKnownTypeWithName(gv.WithKind("TLSRoute"), &TLSRoute{})
}

// +gotohelm:ignore=true
Expand Down Expand Up @@ -167,6 +178,14 @@ func renderResources(state *RenderState) []kube.Object {
manifests = append(manifests, obj)
}

for _, obj := range GatewayServices(state) {
manifests = append(manifests, obj)
}

for _, obj := range TLSRoutes(state) {
manifests = append(manifests, obj)
}

for _, obj := range Secrets(state) {
manifests = append(manifests, obj)
}
Expand Down
12 changes: 12 additions & 0 deletions charts/redpanda/chart/templates/_chart.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $_, $obj := (get (fromJson (include "redpanda.GatewayServices" (dict "a" (list $state)))) "r") -}}
{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $_, $obj := (get (fromJson (include "redpanda.TLSRoutes" (dict "a" (list $state)))) "r") -}}
{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $_, $obj := (get (fromJson (include "redpanda.Secrets" (dict "a" (list $state)))) "r") -}}
{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}}
{{- end -}}
Expand Down
57 changes: 48 additions & 9 deletions charts/redpanda/chart/templates/_secrets.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ echo "passed"`) -}}
{{- $port = (index $externalVals.advertisedPorts $replicaIndex) -}}
{{- end -}}
{{- end -}}
{{- $host := (get (fromJson (include "redpanda.advertisedHostJSON" (dict "a" (list $state $externalName $port $replicaIndex)))) "r") -}}
{{- $host := (get (fromJson (include "redpanda.advertisedHostJSON" (dict "a" (list $state $port $replicaIndex (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.hostTemplate "")))) "r") (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $externalVals)))) "r"))))) "r") -}}
{{- $_ := (set $host "name" $externalName) -}}
{{- $address := (toJson $host) -}}
{{- $prefixTemplate := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.prefixTemplate "")))) "r") -}}
{{- if (eq $prefixTemplate "") -}}
Expand Down Expand Up @@ -278,7 +279,8 @@ echo "passed"`) -}}
{{- $port = (index $externalVals.advertisedPorts $replicaIndex) -}}
{{- end -}}
{{- end -}}
{{- $host := (get (fromJson (include "redpanda.advertisedHostJSON" (dict "a" (list $state $externalName $port $replicaIndex)))) "r") -}}
{{- $host := (get (fromJson (include "redpanda.advertisedHostJSON" (dict "a" (list $state $port $replicaIndex (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.hostTemplate "")))) "r") (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $externalVals)))) "r"))))) "r") -}}
{{- $_ := (set $host "name" $externalName) -}}
{{- $address := (toJson $host) -}}
{{- $prefixTemplate := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $externalVals.prefixTemplate "")))) "r") -}}
{{- if (eq $prefixTemplate "") -}}
Expand Down Expand Up @@ -341,12 +343,19 @@ echo "passed"`) -}}

{{- define "redpanda.advertisedHostJSON" -}}
{{- $state := (index .a 0) -}}
{{- $externalName := (index .a 1) -}}
{{- $port := (index .a 2) -}}
{{- $replicaIndex := (index .a 3) -}}
{{- $port := (index .a 1) -}}
{{- $replicaIndex := (index .a 2) -}}
{{- $host := (index .a 3) -}}
{{- $hostTemplate := (index .a 4) -}}
{{- $isGateway := (index .a 5) -}}
{{- range $_ := (list 1) -}}
{{- $_is_returning := false -}}
{{- $host := (dict "name" $externalName "address" (get (fromJson (include "redpanda.externalAdvertiseAddress" (dict "a" (list $state)))) "r") "port" $port) -}}
{{- if (and (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") $isGateway) -}}
{{- $_is_returning = true -}}
{{- (dict "r" (get (fromJson (include "redpanda.advertisedHostJSONGateway" (dict "a" (list $state $replicaIndex $host $hostTemplate)))) "r")) | toJson -}}
{{- break -}}
{{- end -}}
{{- $hostMap := (dict "name" "" "address" (get (fromJson (include "redpanda.externalAdvertiseAddress" (dict "a" (list $state)))) "r") "port" $port) -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $state.Values.external.addresses)))) "r") | int) (0 | int)) -}}
{{- $address := "" -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $state.Values.external.addresses)))) "r") | int) (1 | int)) -}}
Expand All @@ -356,13 +365,43 @@ echo "passed"`) -}}
{{- end -}}
{{- $domain_5 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $state.Values.external.domain "")))) "r") -}}
{{- if (ne $domain_5 "") -}}
{{- $host = (dict "name" $externalName "address" (printf "%s.%s" $address (tpl $domain_5 $state.Dot)) "port" $port) -}}
{{- $hostMap = (dict "name" "" "address" (printf "%s.%s" $address (tpl $domain_5 $state.Dot)) "port" $port) -}}
{{- else -}}
{{- $host = (dict "name" $externalName "address" $address "port" $port) -}}
{{- $hostMap = (dict "name" "" "address" $address "port" $port) -}}
{{- end -}}
{{- end -}}
{{- $_is_returning = true -}}
{{- (dict "r" $hostMap) | toJson -}}
{{- break -}}
{{- end -}}
{{- end -}}

{{- define "redpanda.advertisedHostJSONGateway" -}}
{{- $state := (index .a 0) -}}
{{- $replicaIndex := (index .a 1) -}}
{{- $host := (index .a 2) -}}
{{- $hostTemplate := (index .a 3) -}}
{{- range $_ := (list 1) -}}
{{- $_is_returning := false -}}
{{- $gw := $state.Values.external.gateway -}}
{{- $port := ((get (fromJson (include "redpanda.GatewayConfig.GatewayAdvertisedPort" (dict "a" (list $gw)))) "r") | int) -}}
{{- if (eq $hostTemplate "") -}}
{{- $hostTemplate = $host -}}
{{- end -}}
{{- $pods := (get (fromJson (include "redpanda.PodNames" (dict "a" (list $state (mustMergeOverwrite (dict "Name" "" "Generation" "" "Statefulset" (dict "additionalSelectorLabels" (coalesce nil) "replicas" 0 "updateStrategy" (dict) "additionalRedpandaCmdFlags" (coalesce nil) "podTemplate" (dict) "budget" (dict "maxUnavailable" 0) "podAntiAffinity" (dict "topologyKey" "" "type" "" "weight" 0 "custom" (coalesce nil)) "sideCars" (dict "image" (dict "repository" "" "tag" "") "args" (coalesce nil) "pvcUnbinder" (dict "enabled" false "unbindAfter" "") "brokerDecommissioner" (dict "enabled" false "decommissionAfter" "" "decommissionRequeueTimeout" "") "configWatcher" (dict "enabled" false) "controllers" (dict "image" (coalesce nil) "enabled" false "createRBAC" false "healthProbeAddress" "" "metricsAddress" "" "pprofAddress" "" "run" (coalesce nil))) "initContainers" (dict "fsValidator" (dict "enabled" false "expectedFS" "") "setDataDirOwnership" (dict "enabled" false) "configurator" (dict)) "initContainerImage" (dict "repository" "" "tag" "")) "ServiceAnnotations" (coalesce nil)) (dict "Statefulset" $state.Values.statefulset)))))) "r") -}}
{{- range $_, $set := $state.Pools -}}
{{- $pods = (concat (default (list) $pods) (default (list) (get (fromJson (include "redpanda.PodNames" (dict "a" (list $state $set)))) "r"))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- $podName := "" -}}
{{- if (lt $replicaIndex ((get (fromJson (include "_shims.len" (dict "a" (list $pods)))) "r") | int)) -}}
{{- $podName = (index $pods $replicaIndex) -}}
{{- end -}}
{{- $address := (get (fromJson (include "redpanda.renderBrokerHost" (dict "a" (list $hostTemplate $replicaIndex $podName)))) "r") -}}
{{- $_is_returning = true -}}
{{- (dict "r" $host) | toJson -}}
{{- (dict "r" (dict "name" "" "address" $address "port" $port)) | toJson -}}
{{- break -}}
{{- end -}}
{{- end -}}
Expand Down
98 changes: 98 additions & 0 deletions charts/redpanda/chart/templates/_service.gateway.go.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{{- /* GENERATED FILE DO NOT EDIT */ -}}
{{- /* Transpiled by gotohelm from "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/service.gateway.go" */ -}}

{{- define "redpanda.GatewayServices" -}}
{{- $state := (index .a 0) -}}
{{- range $_ := (list 1) -}}
{{- $_is_returning := false -}}
{{- if (not (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r")) -}}
{{- $_is_returning = true -}}
{{- (dict "r" (coalesce nil)) | toJson -}}
{{- break -}}
{{- end -}}
{{- $labels := (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") -}}
{{- $selector := (get (fromJson (include "redpanda.ClusterPodLabelsSelector" (dict "a" (list $state)))) "r") -}}
{{- $ports := (get (fromJson (include "redpanda.gatewayServicePorts" (dict "a" (list $state)))) "r") -}}
{{- if (eq ((get (fromJson (include "_shims.len" (dict "a" (list $ports)))) "r") | int) (0 | int)) -}}
{{- $_is_returning = true -}}
{{- (dict "r" (coalesce nil)) | toJson -}}
{{- break -}}
{{- end -}}
{{- $services := (coalesce nil) -}}
{{- $bootstrap := (mustMergeOverwrite (dict "metadata" (dict) "spec" (dict) "status" (dict "loadBalancer" (dict))) (mustMergeOverwrite (dict) (dict "apiVersion" "v1" "kind" "Service")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (printf "%s-gateway-bootstrap" (get (fromJson (include "redpanda.Fullname" (dict "a" (list $state)))) "r")) "namespace" $state.Release.Namespace "labels" $labels)) "spec" (mustMergeOverwrite (dict) (dict "ports" $ports "publishNotReadyAddresses" true "selector" $selector "sessionAffinity" "None" "type" "ClusterIP")))) -}}
{{- $services = (concat (default (list) $services) (list $bootstrap)) -}}
{{- $pods := (get (fromJson (include "redpanda.PodNames" (dict "a" (list $state (mustMergeOverwrite (dict "Name" "" "Generation" "" "Statefulset" (dict "additionalSelectorLabels" (coalesce nil) "replicas" 0 "updateStrategy" (dict) "additionalRedpandaCmdFlags" (coalesce nil) "podTemplate" (dict) "budget" (dict "maxUnavailable" 0) "podAntiAffinity" (dict "topologyKey" "" "type" "" "weight" 0 "custom" (coalesce nil)) "sideCars" (dict "image" (dict "repository" "" "tag" "") "args" (coalesce nil) "pvcUnbinder" (dict "enabled" false "unbindAfter" "") "brokerDecommissioner" (dict "enabled" false "decommissionAfter" "" "decommissionRequeueTimeout" "") "configWatcher" (dict "enabled" false) "controllers" (dict "image" (coalesce nil) "enabled" false "createRBAC" false "healthProbeAddress" "" "metricsAddress" "" "pprofAddress" "" "run" (coalesce nil))) "initContainers" (dict "fsValidator" (dict "enabled" false "expectedFS" "") "setDataDirOwnership" (dict "enabled" false) "configurator" (dict)) "initContainerImage" (dict "repository" "" "tag" "")) "ServiceAnnotations" (coalesce nil)) (dict "Statefulset" $state.Values.statefulset)))))) "r") -}}
{{- range $_, $set := $state.Pools -}}
{{- $pods = (concat (default (list) $pods) (default (list) (get (fromJson (include "redpanda.PodNames" (dict "a" (list $state $set)))) "r"))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $_, $podname := $pods -}}
{{- $podSelector := (dict) -}}
{{- range $k, $v := $selector -}}
{{- $_ := (set $podSelector $k $v) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- $_ := (set $podSelector "statefulset.kubernetes.io/pod-name" $podname) -}}
{{- $svc := (mustMergeOverwrite (dict "metadata" (dict) "spec" (dict) "status" (dict "loadBalancer" (dict))) (mustMergeOverwrite (dict) (dict "apiVersion" "v1" "kind" "Service")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (printf "gw-%s" $podname) "namespace" $state.Release.Namespace "labels" $labels)) "spec" (mustMergeOverwrite (dict) (dict "ports" $ports "publishNotReadyAddresses" true "selector" $podSelector "sessionAffinity" "None" "type" "ClusterIP")))) -}}
{{- $services = (concat (default (list) $services) (list $svc)) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- $_is_returning = true -}}
{{- (dict "r" $services) | toJson -}}
{{- break -}}
{{- end -}}
{{- end -}}

{{- define "redpanda.gatewayServicePorts" -}}
{{- $state := (index .a 0) -}}
{{- range $_ := (list 1) -}}
{{- $_is_returning := false -}}
{{- $ports := (coalesce nil) -}}
{{- range $name, $listener := $state.Values.listeners.admin.external -}}
{{- if (or (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) (not (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r"))) -}}
{{- continue -}}
{{- end -}}
{{- $ports = (concat (default (list) $ports) (list (mustMergeOverwrite (dict "port" 0 "targetPort" 0) (dict "name" (printf "admin-%s" $name) "protocol" "TCP" "port" ($listener.port | int))))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $name, $listener := $state.Values.listeners.kafka.external -}}
{{- if (or (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) (not (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r"))) -}}
{{- continue -}}
{{- end -}}
{{- $ports = (concat (default (list) $ports) (list (mustMergeOverwrite (dict "port" 0 "targetPort" 0) (dict "name" (printf "kafka-%s" $name) "protocol" "TCP" "port" ($listener.port | int))))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $name, $listener := $state.Values.listeners.http.external -}}
{{- if (or (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) (not (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r"))) -}}
{{- continue -}}
{{- end -}}
{{- $ports = (concat (default (list) $ports) (list (mustMergeOverwrite (dict "port" 0 "targetPort" 0) (dict "name" (printf "http-%s" $name) "protocol" "TCP" "port" ($listener.port | int))))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- range $name, $listener := $state.Values.listeners.schemaRegistry.external -}}
{{- if (or (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) (not (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r"))) -}}
{{- continue -}}
{{- end -}}
{{- $ports = (concat (default (list) $ports) (list (mustMergeOverwrite (dict "port" 0 "targetPort" 0) (dict "name" (printf "schema-%s" $name) "protocol" "TCP" "port" ($listener.port | int))))) -}}
{{- end -}}
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- $_is_returning = true -}}
{{- (dict "r" $ports) | toJson -}}
{{- break -}}
{{- end -}}
{{- end -}}

18 changes: 18 additions & 0 deletions charts/redpanda/chart/templates/_service.nodeport.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
{{- (dict "r" (coalesce nil)) | toJson -}}
{{- break -}}
{{- end -}}
{{- $gwEnabled := (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}}
{{- $ports := (coalesce nil) -}}
{{- range $name, $listener := $state.Values.listeners.admin.external -}}
{{- if (not (get (fromJson (include "redpanda.ExternalListener.IsEnabled" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- if (and $gwEnabled (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- $nodePort := ($listener.port | int) -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $listener.advertisedPorts)))) "r") | int) (0 | int)) -}}
{{- $nodePort = (index $listener.advertisedPorts (0 | int)) -}}
Expand All @@ -33,6 +37,9 @@
{{- if (not (get (fromJson (include "redpanda.ExternalListener.IsEnabled" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- if (and $gwEnabled (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- $nodePort := ($listener.port | int) -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $listener.advertisedPorts)))) "r") | int) (0 | int)) -}}
{{- $nodePort = (index $listener.advertisedPorts (0 | int)) -}}
Expand All @@ -46,6 +53,9 @@
{{- if (not (get (fromJson (include "redpanda.ExternalListener.IsEnabled" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- if (and $gwEnabled (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- $nodePort := ($listener.port | int) -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $listener.advertisedPorts)))) "r") | int) (0 | int)) -}}
{{- $nodePort = (index $listener.advertisedPorts (0 | int)) -}}
Expand All @@ -59,6 +69,9 @@
{{- if (not (get (fromJson (include "redpanda.ExternalListener.IsEnabled" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- if (and $gwEnabled (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $listener)))) "r")) -}}
{{- continue -}}
{{- end -}}
{{- $nodePort := ($listener.port | int) -}}
{{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $listener.advertisedPorts)))) "r") | int) (0 | int)) -}}
{{- $nodePort = (index $listener.advertisedPorts (0 | int)) -}}
Expand All @@ -68,6 +81,11 @@
{{- if $_is_returning -}}
{{- break -}}
{{- end -}}
{{- if (eq ((get (fromJson (include "_shims.len" (dict "a" (list $ports)))) "r") | int) (0 | int)) -}}
{{- $_is_returning = true -}}
{{- (dict "r" (coalesce nil)) | toJson -}}
{{- break -}}
{{- end -}}
{{- $annotations := $state.Values.external.annotations -}}
{{- if (eq (toJson $annotations) "null") -}}
{{- $annotations = (dict) -}}
Expand Down
Loading
Loading