From 3aff314f889d9cb793dd02b5b1f51cb70293f2f7 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 17:13:11 -0700 Subject: [PATCH 01/13] charts/redpanda: Add Gateway API TLSRoute support for external access Adds a new external access mode using Gateway API TLSRoute resources with SNI-based routing, enabling per-listener domain configuration and removing the need for NodePort/LoadBalancer services. Design: - User provides their own Gateway; the chart only manages TLSRoutes - Bootstrap ClusterIP service + per-broker ClusterIP services created as TLSRoute backends - Per-listener host/hostTemplate fields for SNI hostname configuration - Each external listener gets a bootstrap TLSRoute and per-broker TLSRoutes with unique SNI hostnames - Default advertised port is 443 (configurable via gateway.advertisedPort) - NodePort/LoadBalancer service generation is skipped in gateway mode Example values: external: enabled: true gateway: enabled: true parentRefs: - name: kafka-gateway sectionName: kafka listeners: kafka: external: default: port: 9094 host: kafka.example.com hostTemplate: kafka-$POD_ORDINAL.example.com Closes #1361 Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/chart.go | 11 + charts/redpanda/secrets.go | 76 +++++ charts/redpanda/service.gateway.go | 151 ++++++++++ charts/redpanda/service.loadbalancer.go | 5 + charts/redpanda/service.nodeport.go | 5 + charts/redpanda/tlsroute.go | 277 ++++++++++++++++++ charts/redpanda/values.go | 66 +++++ .../v1alpha2/redpanda_clusterspec_types.go | 30 ++ .../redpanda/redpanda_controller.go | 3 + 9 files changed, 624 insertions(+) create mode 100644 charts/redpanda/service.gateway.go create mode 100644 charts/redpanda/tlsroute.go diff --git a/charts/redpanda/chart.go b/charts/redpanda/chart.go index f640527c9..19874005f 100644 --- a/charts/redpanda/chart.go +++ b/charts/redpanda/chart.go @@ -25,6 +25,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" consolechart "github.com/redpanda-data/redpanda-operator/charts/console/v3/chart" redpandachart "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/chart" @@ -56,6 +57,7 @@ func Types() []kube.Object { &corev1.Secret{}, &corev1.ServiceAccount{}, &corev1.Service{}, + &gatewayv1alpha2.TLSRoute{}, &monitoringv1.PodMonitor{}, &monitoringv1.ServiceMonitor{}, &networkingv1.Ingress{}, @@ -71,6 +73,7 @@ func Types() []kube.Object { func init() { must(scheme.AddToScheme(Scheme)) must(certmanagerv1.AddToScheme(Scheme)) + must(gatewayv1alpha2.Install(Scheme)) must(monitoringv1.AddToScheme(Scheme)) } @@ -167,6 +170,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) } diff --git a/charts/redpanda/secrets.go b/charts/redpanda/secrets.go index 5fababb8b..6e54308dc 100644 --- a/charts/redpanda/secrets.go +++ b/charts/redpanda/secrets.go @@ -538,6 +538,12 @@ func externalAdvertiseAddress(state *RenderState) string { // was advertised-host func advertisedHostJSON(state *RenderState, externalName string, port int32, replicaIndex int) map[string]any { + // Gateway API mode: advertise the TLSRoute SNI hostname and the + // gateway's advertised port (default 443) rather than a NodePort/LB address. + if state.Values.External.IsGatewayEnabled() { + return advertisedHostJSONGateway(state, externalName, replicaIndex) + } + host := map[string]any{ "name": externalName, "address": externalAdvertiseAddress(state), @@ -567,6 +573,76 @@ func advertisedHostJSON(state *RenderState, externalName string, port int32, rep return host } +// advertisedHostJSONGateway builds the advertised host entry for Gateway API +// mode. The address is the per-broker SNI hostname (from HostTemplate) and the +// port is the gateway's advertised port (default 443). +func advertisedHostJSONGateway(state *RenderState, externalName string, replicaIndex int) map[string]any { + gw := state.Values.External.Gateway + port := gw.GatewayAdvertisedPort() + + // Look up the listener's HostTemplate to build the per-broker address. + // We search all listener types for the matching external name. + hostTemplate := findListenerHostTemplate(state, externalName) + if hostTemplate == "" { + // Fallback: use the bootstrap host if no template is set. + hostTemplate = findListenerHost(state, externalName) + } + + pods := PodNames(state, Pool{Statefulset: state.Values.Statefulset}) + for _, set := range state.Pools { + pods = append(pods, PodNames(state, set)...) + } + + podName := "" + if replicaIndex < len(pods) { + podName = pods[replicaIndex] + } + + address := renderBrokerHost(hostTemplate, replicaIndex, podName) + + return map[string]any{ + "name": externalName, + "address": address, + "port": port, + } +} + +// findListenerHostTemplate searches all listener types for an external listener +// with the given name and returns its HostTemplate. +func findListenerHostTemplate(state *RenderState, name string) string { + if l, ok := state.Values.Listeners.Kafka.External[name]; ok { + return ptr.Deref(l.HostTemplate, "") + } + if l, ok := state.Values.Listeners.HTTP.External[name]; ok { + return ptr.Deref(l.HostTemplate, "") + } + if l, ok := state.Values.Listeners.Admin.External[name]; ok { + return ptr.Deref(l.HostTemplate, "") + } + if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { + return ptr.Deref(l.HostTemplate, "") + } + return "" +} + +// findListenerHost searches all listener types for an external listener with +// the given name and returns its Host (bootstrap hostname). +func findListenerHost(state *RenderState, name string) string { + if l, ok := state.Values.Listeners.Kafka.External[name]; ok { + return ptr.Deref(l.Host, "") + } + if l, ok := state.Values.Listeners.HTTP.External[name]; ok { + return ptr.Deref(l.Host, "") + } + if l, ok := state.Values.Listeners.Admin.External[name]; ok { + return ptr.Deref(l.Host, "") + } + if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { + return ptr.Deref(l.Host, "") + } + return "" +} + // adminInternalHTTPProtocol was admin-http-protocol func adminInternalHTTPProtocol(state *RenderState) string { if state.Values.Listeners.Admin.TLS.IsEnabled(&state.Values.TLS) { diff --git a/charts/redpanda/service.gateway.go b/charts/redpanda/service.gateway.go new file mode 100644 index 000000000..26fd5a65c --- /dev/null +++ b/charts/redpanda/service.gateway.go @@ -0,0 +1,151 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +// +gotohelm:filename=_service.gateway.go.tpl +package redpanda + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" +) + +// GatewayServices returns ClusterIP Services for Gateway API TLSRoute-based +// external access: one bootstrap service (targeting all pods) and one +// per-broker service (targeting a specific pod via pod-name selector). +// TLSRoute resources reference these services as backends. +func GatewayServices(state *RenderState) []*corev1.Service { + if !state.Values.External.IsGatewayEnabled() { + return nil + } + + labels := FullLabels(state) + selector := ClusterPodLabelsSelector(state) + + // Collect external listener ports across all listener types. + ports := gatewayServicePorts(state) + if len(ports) == 0 { + return nil + } + + var services []*corev1.Service + + // Bootstrap service: targets all pods for initial client connection. + bootstrap := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-gateway-bootstrap", Fullname(state)), + Namespace: state.Release.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: ports, + PublishNotReadyAddresses: true, + Selector: selector, + SessionAffinity: corev1.ServiceAffinityNone, + Type: corev1.ServiceTypeClusterIP, + }, + } + services = append(services, bootstrap) + + // Per-broker services: one service per pod, selected by pod name. + pods := PodNames(state, Pool{Statefulset: state.Values.Statefulset}) + for _, set := range state.Pools { + pods = append(pods, PodNames(state, set)...) + } + + for _, podname := range pods { + podSelector := map[string]string{} + for k, v := range selector { + podSelector[k] = v + } + podSelector["statefulset.kubernetes.io/pod-name"] = podname + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("gw-%s", podname), + Namespace: state.Release.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: ports, + PublishNotReadyAddresses: true, + Selector: podSelector, + SessionAffinity: corev1.ServiceAffinityNone, + Type: corev1.ServiceTypeClusterIP, + }, + } + services = append(services, svc) + } + + return services +} + +// gatewayServicePorts collects external listener ports for Gateway ClusterIP +// services. These match the container ports that TLSRoutes will route to. +func gatewayServicePorts(state *RenderState) []corev1.ServicePort { + var ports []corev1.ServicePort + + for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + ports = append(ports, corev1.ServicePort{ + Name: fmt.Sprintf("admin-%s", name), + Protocol: corev1.ProtocolTCP, + Port: listener.Port, + }) + } + + for name, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + ports = append(ports, corev1.ServicePort{ + Name: fmt.Sprintf("kafka-%s", name), + Protocol: corev1.ProtocolTCP, + Port: listener.Port, + }) + } + + for name, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + ports = append(ports, corev1.ServicePort{ + Name: fmt.Sprintf("http-%s", name), + Protocol: corev1.ProtocolTCP, + Port: listener.Port, + }) + } + + for name, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + ports = append(ports, corev1.ServicePort{ + Name: fmt.Sprintf("schema-%s", name), + Protocol: corev1.ProtocolTCP, + Port: listener.Port, + }) + } + + return ports +} diff --git a/charts/redpanda/service.loadbalancer.go b/charts/redpanda/service.loadbalancer.go index bf5fca8a8..f0cc63955 100644 --- a/charts/redpanda/service.loadbalancer.go +++ b/charts/redpanda/service.loadbalancer.go @@ -27,6 +27,11 @@ func LoadBalancerServices(state *RenderState) []*corev1.Service { return nil } + // Gateway API mode uses its own ClusterIP services and TLSRoutes. + if state.Values.External.IsGatewayEnabled() { + return nil + } + if state.Values.External.Type != corev1.ServiceTypeLoadBalancer { return nil } diff --git a/charts/redpanda/service.nodeport.go b/charts/redpanda/service.nodeport.go index 612538ee4..cc2aff4dc 100644 --- a/charts/redpanda/service.nodeport.go +++ b/charts/redpanda/service.nodeport.go @@ -24,6 +24,11 @@ func NodePortService(state *RenderState) *corev1.Service { return nil } + // Gateway API mode uses its own ClusterIP services and TLSRoutes. + if state.Values.External.IsGatewayEnabled() { + return nil + } + if state.Values.External.Type != corev1.ServiceTypeNodePort { return nil } diff --git a/charts/redpanda/tlsroute.go b/charts/redpanda/tlsroute.go new file mode 100644 index 000000000..97995076e --- /dev/null +++ b/charts/redpanda/tlsroute.go @@ -0,0 +1,277 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +// +gotohelm:filename=_tlsroute.go.tpl +package redpanda + +import ( + "fmt" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" +) + +// TLSRoutes returns Gateway API TLSRoute resources for external access. +// +// For each enabled external listener (across Kafka, HTTP, Admin, +// SchemaRegistry), this creates: +// - A bootstrap TLSRoute pointing to the bootstrap ClusterIP service +// - Per-broker TLSRoutes pointing to per-broker ClusterIP services +// +// SNI hostnames on each TLSRoute enable the Gateway to route traffic to the +// correct broker based on the client-requested hostname. +func TLSRoutes(state *RenderState) []*gatewayv1alpha2.TLSRoute { + if !state.Values.External.IsGatewayEnabled() { + return nil + } + + gw := state.Values.External.Gateway + parentRefs := toGatewayParentRefs(gw.ParentRefs) + labels := FullLabels(state) + fullname := Fullname(state) + + pods := PodNames(state, Pool{Statefulset: state.Values.Statefulset}) + for _, set := range state.Pools { + pods = append(pods, PodNames(state, set)...) + } + + var routes []*gatewayv1alpha2.TLSRoute + + // Kafka listeners + for name, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + rs := tlsRoutesForListener(tlsRouteParams{ + fullname: fullname, + namespace: state.Release.Namespace, + labels: labels, + parentRefs: parentRefs, + pods: pods, + host: ptr.Deref(listener.Host, ""), + hostTemplate: ptr.Deref(listener.HostTemplate, ""), + name: name, + listenerTag: "kafka", + port: listener.Port, + }) + routes = append(routes, rs...) + } + + // HTTP/Pandaproxy listeners + for name, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + rs := tlsRoutesForListener(tlsRouteParams{ + fullname: fullname, + namespace: state.Release.Namespace, + labels: labels, + parentRefs: parentRefs, + pods: pods, + host: ptr.Deref(listener.Host, ""), + hostTemplate: ptr.Deref(listener.HostTemplate, ""), + name: name, + listenerTag: "http", + port: listener.Port, + }) + routes = append(routes, rs...) + } + + // Admin listeners + for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + rs := tlsRoutesForListener(tlsRouteParams{ + fullname: fullname, + namespace: state.Release.Namespace, + labels: labels, + parentRefs: parentRefs, + pods: pods, + host: ptr.Deref(listener.Host, ""), + hostTemplate: ptr.Deref(listener.HostTemplate, ""), + name: name, + listenerTag: "admin", + port: listener.Port, + }) + routes = append(routes, rs...) + } + + // Schema Registry listeners + for name, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + continue + } + rs := tlsRoutesForListener(tlsRouteParams{ + fullname: fullname, + namespace: state.Release.Namespace, + labels: labels, + parentRefs: parentRefs, + pods: pods, + host: ptr.Deref(listener.Host, ""), + hostTemplate: ptr.Deref(listener.HostTemplate, ""), + name: name, + listenerTag: "schema", + port: listener.Port, + }) + routes = append(routes, rs...) + } + + return routes +} + +type tlsRouteParams struct { + fullname string + namespace string + labels map[string]string + parentRefs []gatewayv1.ParentReference + pods []string + host string + hostTemplate string + name string + listenerTag string + port int32 +} + +func tlsRoutesForListener(p tlsRouteParams) []*gatewayv1alpha2.TLSRoute { + var routes []*gatewayv1alpha2.TLSRoute + + if p.host == "" { + return nil + } + + bootstrapSvcName := fmt.Sprintf("%s-gateway-bootstrap", p.fullname) + + // Bootstrap TLSRoute: routes initial client connections to any broker. + bootstrap := &gatewayv1alpha2.TLSRoute{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "gateway.networking.k8s.io/v1alpha2", + Kind: "TLSRoute", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-%s-bootstrap", p.fullname, p.listenerTag, p.name), + Namespace: p.namespace, + Labels: p.labels, + }, + Spec: gatewayv1alpha2.TLSRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: p.parentRefs, + }, + Hostnames: []gatewayv1.Hostname{ + gatewayv1.Hostname(p.host), + }, + Rules: []gatewayv1alpha2.TLSRouteRule{ + { + BackendRefs: []gatewayv1.BackendRef{ + { + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(bootstrapSvcName), + Port: portPtr(p.port), + }, + }, + }, + }, + }, + }, + } + routes = append(routes, bootstrap) + + // Per-broker TLSRoutes: each broker gets a unique SNI hostname so the + // Gateway can route directly to the correct pod. + if p.hostTemplate == "" { + return routes + } + + for i, podname := range p.pods { + brokerHost := renderBrokerHost(p.hostTemplate, i, podname) + brokerSvcName := fmt.Sprintf("gw-%s", podname) + + route := &gatewayv1alpha2.TLSRoute{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "gateway.networking.k8s.io/v1alpha2", + Kind: "TLSRoute", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-%s-%d", p.fullname, p.listenerTag, p.name, i), + Namespace: p.namespace, + Labels: p.labels, + }, + Spec: gatewayv1alpha2.TLSRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: p.parentRefs, + }, + Hostnames: []gatewayv1.Hostname{ + gatewayv1.Hostname(brokerHost), + }, + Rules: []gatewayv1alpha2.TLSRouteRule{ + { + BackendRefs: []gatewayv1.BackendRef{ + { + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(brokerSvcName), + Port: portPtr(p.port), + }, + }, + }, + }, + }, + }, + } + routes = append(routes, route) + } + + return routes +} + +// toGatewayParentRefs converts the chart's GatewayParentRef values into the +// Gateway API ParentReference type. +func toGatewayParentRefs(refs []GatewayParentRef) []gatewayv1.ParentReference { + var parentRefs []gatewayv1.ParentReference + for _, ref := range refs { + pr := gatewayv1.ParentReference{ + Name: gatewayv1.ObjectName(ref.Name), + } + if ref.Group != nil { + g := gatewayv1.Group(*ref.Group) + pr.Group = &g + } + if ref.Kind != nil { + k := gatewayv1.Kind(*ref.Kind) + pr.Kind = &k + } + if ref.Namespace != nil { + ns := gatewayv1.Namespace(*ref.Namespace) + pr.Namespace = &ns + } + if ref.SectionName != nil { + sn := gatewayv1.SectionName(*ref.SectionName) + pr.SectionName = &sn + } + parentRefs = append(parentRefs, pr) + } + return parentRefs +} + +// renderBrokerHost interpolates the host template with broker-specific values. +// Supports $POD_ORDINAL and $POD_NAME. +func renderBrokerHost(tmpl string, ordinal int, podName string) string { + result := strings.ReplaceAll(tmpl, "$POD_ORDINAL", fmt.Sprintf("%d", ordinal)) + result = strings.ReplaceAll(result, "$POD_NAME", podName) + return result +} + +func portPtr(port int32) *gatewayv1.PortNumber { + p := gatewayv1.PortNumber(port) + return &p +} diff --git a/charts/redpanda/values.go b/charts/redpanda/values.go index 56346efc7..105b7921b 100644 --- a/charts/redpanda/values.go +++ b/charts/redpanda/values.go @@ -260,6 +260,61 @@ type ExternalConfig struct { SourceRanges []string `json:"sourceRanges"` Service Enableable `json:"service"` ExternalDNS *Enableable `json:"externalDns"` + // Gateway configures Gateway API TLSRoute-based external access. + // When enabled, ClusterIP services and TLSRoute resources are created + // instead of NodePort/LoadBalancer services. The Gateway itself must be + // managed separately; the chart only creates TLSRoute resources that + // reference it. + Gateway *GatewayConfig `json:"gateway,omitempty"` +} + +// GatewayConfig holds configuration for Gateway API-based external access +// using TLSRoute resources with SNI-based routing. +type GatewayConfig struct { + // Enabled activates Gateway API TLSRoute-based external access. When true, + // this takes precedence over the NodePort/LoadBalancer external.type setting. + Enabled bool `json:"enabled"` + // ParentRefs defines which Gateway(s) handle the TLSRoutes. At least one + // parent reference must be provided. These are passed directly into each + // TLSRoute's spec.parentRefs. + ParentRefs []GatewayParentRef `json:"parentRefs"` + // AdvertisedPort is the port advertised to clients. Defaults to 443 + // because the actual listening port is configured on the Gateway, not + // on the TLSRoute. + AdvertisedPort *int32 `json:"advertisedPort,omitempty"` +} + +// GatewayParentRef identifies a Gateway (or ListenerSet) that should handle +// the TLSRoute traffic. The schema mirrors the upstream Gateway API +// ParentReference so users see familiar field names. +type GatewayParentRef struct { + // Group is the API group of the referent. Defaults to + // "gateway.networking.k8s.io". + Group *string `json:"group,omitempty"` + // Kind is the kind of the referent. Defaults to "Gateway". + Kind *string `json:"kind,omitempty"` + // Name is the name of the referent. + Name string `json:"name"` + // Namespace is the namespace of the referent. When unspecified, refers + // to the local namespace of the TLSRoute. + Namespace *string `json:"namespace,omitempty"` + // SectionName is the name of a section within the target resource. + SectionName *string `json:"sectionName,omitempty"` +} + +// IsGatewayEnabled returns true when Gateway API TLSRoute-based external +// access is configured and enabled. +func (e *ExternalConfig) IsGatewayEnabled() bool { + return e.Enabled && e.Gateway != nil && e.Gateway.Enabled && len(e.Gateway.ParentRefs) > 0 +} + +// GatewayAdvertisedPort returns the port to advertise to clients when using +// Gateway API. Defaults to 443. +func (g *GatewayConfig) GatewayAdvertisedPort() int32 { + if g.AdvertisedPort != nil { + return *g.AdvertisedPort + } + return 443 } type Enableable struct { @@ -1773,6 +1828,15 @@ type ExternalListener[T ~string] struct { AuthenticationMethod *T `json:"authenticationMethod,omitempty"` PrefixTemplate *string `json:"prefixTemplate,omitempty"` + + // Host is the SNI hostname used when external.gateway is enabled. + // Used as the hostname in the bootstrap TLSRoute. For per-broker + // TLSRoutes, the pod ordinal is interpolated via HostTemplate. + Host *string `json:"host,omitempty"` + // HostTemplate is a Go template for per-broker TLSRoute hostnames. + // Available variables: $POD_ORDINAL, $POD_NAME. + // Example: "kafka-{{$POD_ORDINAL}}-broker.example.com" + HostTemplate *string `json:"hostTemplate,omitempty"` } func (l *ExternalListener[T]) AsString() ExternalListener[string] { @@ -1790,6 +1854,8 @@ func (l *ExternalListener[T]) AsString() ExternalListener[string] { TLS: l.TLS, AuthenticationMethod: auth, PrefixTemplate: l.PrefixTemplate, + Host: l.Host, + HostTemplate: l.HostTemplate, } } diff --git a/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go b/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go index 150338b8a..ba055b6e7 100644 --- a/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go +++ b/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go @@ -520,6 +520,32 @@ type External struct { ExternalDNS *ExternalDNS `json:"externalDns,omitempty"` // Specifies a naming prefix template for external Services. PrefixTemplate *string `json:"prefixTemplate,omitempty"` + // Configures Gateway API TLSRoute-based external access. When enabled, ClusterIP services and TLSRoute resources are created instead of NodePort/LoadBalancer services. The Gateway itself must be managed externally. + Gateway *GatewayExternalConfig `json:"gateway,omitempty"` +} + +// GatewayExternalConfig holds configuration for Gateway API-based external access using TLSRoute resources with SNI-based routing. +type GatewayExternalConfig struct { + // Enables Gateway API TLSRoute-based external access. + Enabled *bool `json:"enabled,omitempty"` + // Defines which Gateway(s) handle the TLSRoutes. At least one parent reference must be provided. + ParentRefs []GatewayParentRefConfig `json:"parentRefs,omitempty"` + // The port advertised to clients. Defaults to 443. + AdvertisedPort *int32 `json:"advertisedPort,omitempty"` +} + +// GatewayParentRefConfig identifies a Gateway (or ListenerSet) that should handle the TLSRoute traffic. Schema mirrors the upstream Gateway API ParentReference. +type GatewayParentRefConfig struct { + // API group of the referent. Defaults to "gateway.networking.k8s.io". + Group *string `json:"group,omitempty"` + // Kind of the referent. Defaults to "Gateway". + Kind *string `json:"kind,omitempty"` + // Name of the referent. + Name string `json:"name"` + // Namespace of the referent. + Namespace *string `json:"namespace,omitempty"` + // Name of a section within the target resource. + SectionName *string `json:"sectionName,omitempty"` } // Logging configures logging settings in the Helm values. See https://docs.redpanda.com/current/manage/kubernetes/troubleshooting/troubleshoot/. @@ -973,6 +999,10 @@ type ExternalListener struct { // Specifies the network port that the external Service listens on. AdvertisedPorts []int32 `json:"advertisedPorts,omitempty"` NodePort *int32 `json:"nodePort,omitempty"` + // Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. + Host *string `json:"host,omitempty"` + // HostTemplate is a Go template for per-broker TLSRoute SNI hostnames. Supports $POD_ORDINAL and $POD_NAME variables. + HostTemplate *string `json:"hostTemplate,omitempty"` } // Admin configures settings for the Admin API listeners. diff --git a/operator/internal/controller/redpanda/redpanda_controller.go b/operator/internal/controller/redpanda/redpanda_controller.go index 5cffd2fac..8e5d66d4c 100644 --- a/operator/internal/controller/redpanda/redpanda_controller.go +++ b/operator/internal/controller/redpanda/redpanda_controller.go @@ -107,6 +107,9 @@ type RedpandaReconciler struct { // +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cluster.redpanda.com,resources=consoles,verbs=get;list;watch;create;update;patch;delete +// Gateway API TLSRoute resources for external access +// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=tlsroutes,verbs=get;list;watch;create;update;patch;delete + // redpanda resources // +kubebuilder:rbac:groups=cluster.redpanda.com,resources=redpandas,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cluster.redpanda.com,resources=redpandas/status,verbs=get;update;patch From 426b0030c1753a334e583a34bf647fa6478986b9 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 19:43:06 -0700 Subject: [PATCH 02/13] charts/redpanda: Generate templates and golden tests for Gateway API TLSRoute - Transpile Go source to Helm templates via gotohelm - Define lightweight TLSRoute mirror types compatible with gotohelm (upstream Gateway API uses type aliases the transpiler cannot handle) - Register TLSRoute in the chart Scheme for test deserialization - Add gateway-api-tlsroute test case to template-cases.txtar - Regenerate all generated files (CRDs, RBAC, deepcopy, schemas) Golden test output confirms correct resource generation: - Bootstrap TLSRoute with SNI hostname pointing to bootstrap ClusterIP service - Per-broker TLSRoutes with interpolated hostnames pointing to per-broker services - ClusterIP services (bootstrap + per-pod) as TLSRoute backends - NodePort/LoadBalancer services correctly skipped in gateway mode Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/chart.go | 15 +- charts/redpanda/chart/templates/_chart.go.tpl | 12 + .../redpanda/chart/templates/_secrets.go.tpl | 121 ++ .../chart/templates/_service.gateway.go.tpl | 98 + .../templates/_service.loadbalancer.go.tpl | 5 + .../chart/templates/_service.nodeport.go.tpl | 5 + .../redpanda/chart/templates/_tlsroute.go.tpl | 144 ++ .../redpanda/chart/templates/_values.go.tpl | 147 +- charts/redpanda/chart/values.schema.json | 67 + .../testdata/template-cases.golden.txtar | 1652 +++++++++++++++++ charts/redpanda/testdata/template-cases.txtar | 29 + charts/redpanda/tlsroute.go | 242 +-- charts/redpanda/values_partial.gen.go | 35 +- .../redpanda/v1alpha2/external.go | 27 +- .../v1alpha2/gatewayexternalconfig.go | 55 + .../v1alpha2/gatewayparentrefconfig.go | 68 + .../redpanda/v1alpha2/testdata/crd-docs.adoc | 51 + .../v1alpha2/zz_generated.deepcopy.go | 82 + .../zz_generated.deprecations_test.go | 3 +- .../files/rbac/v2-manager.ClusterRole.yaml | 12 + .../bases/cluster.redpanda.com_redpandas.yaml | 162 ++ .../cluster.redpanda.com_stretchclusters.yaml | 42 + operator/config/rbac/bases/operator/role.yaml | 12 + operator/config/rbac/itemized/v2-manager.yaml | 12 + .../controller/redpanda/testdata/role.yaml | 12 + .../internal/statuses/zz_generated_status.go | 2 +- .../proto/gen/transport/v1/message.pb.go | 9 + .../proto/gen/transport/v1/message_grpc.pb.go | 9 + 28 files changed, 2895 insertions(+), 235 deletions(-) create mode 100644 charts/redpanda/chart/templates/_service.gateway.go.tpl create mode 100644 charts/redpanda/chart/templates/_tlsroute.go.tpl create mode 100644 operator/api/applyconfiguration/redpanda/v1alpha2/gatewayexternalconfig.go create mode 100644 operator/api/applyconfiguration/redpanda/v1alpha2/gatewayparentrefconfig.go diff --git a/charts/redpanda/chart.go b/charts/redpanda/chart.go index 19874005f..6a4a1bdcb 100644 --- a/charts/redpanda/chart.go +++ b/charts/redpanda/chart.go @@ -24,8 +24,8 @@ 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" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" consolechart "github.com/redpanda-data/redpanda-operator/charts/console/v3/chart" redpandachart "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/chart" @@ -57,7 +57,7 @@ func Types() []kube.Object { &corev1.Secret{}, &corev1.ServiceAccount{}, &corev1.Service{}, - &gatewayv1alpha2.TLSRoute{}, + &TLSRoute{}, &monitoringv1.PodMonitor{}, &monitoringv1.ServiceMonitor{}, &networkingv1.Ingress{}, @@ -73,8 +73,17 @@ func Types() []kube.Object { func init() { must(scheme.AddToScheme(Scheme)) must(certmanagerv1.AddToScheme(Scheme)) - must(gatewayv1alpha2.Install(Scheme)) must(monitoringv1.AddToScheme(Scheme)) + must(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) error { + gv := schema.GroupVersion{Group: "gateway.networking.k8s.io", Version: "v1alpha2"} + s.AddKnownTypeWithName(gv.WithKind("TLSRoute"), &TLSRoute{}) + return nil } // +gotohelm:ignore=true diff --git a/charts/redpanda/chart/templates/_chart.go.tpl b/charts/redpanda/chart/templates/_chart.go.tpl index eb8c9ecc3..0c80abf3c 100644 --- a/charts/redpanda/chart/templates/_chart.go.tpl +++ b/charts/redpanda/chart/templates/_chart.go.tpl @@ -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 -}} diff --git a/charts/redpanda/chart/templates/_secrets.go.tpl b/charts/redpanda/chart/templates/_secrets.go.tpl index b04d7098a..cf30fc93b 100644 --- a/charts/redpanda/chart/templates/_secrets.go.tpl +++ b/charts/redpanda/chart/templates/_secrets.go.tpl @@ -346,6 +346,11 @@ echo "passed"`) -}} {{- $replicaIndex := (index .a 3) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} +{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "redpanda.advertisedHostJSONGateway" (dict "a" (list $state $externalName $replicaIndex)))) "r")) | toJson -}} +{{- break -}} +{{- end -}} {{- $host := (dict "name" $externalName "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 := "" -}} @@ -367,6 +372,122 @@ echo "passed"`) -}} {{- end -}} {{- end -}} +{{- define "redpanda.advertisedHostJSONGateway" -}} +{{- $state := (index .a 0) -}} +{{- $externalName := (index .a 1) -}} +{{- $replicaIndex := (index .a 2) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $gw := $state.Values.external.gateway -}} +{{- $port := ((get (fromJson (include "redpanda.GatewayConfig.GatewayAdvertisedPort" (dict "a" (list $gw)))) "r") | int) -}} +{{- $hostTemplate := (get (fromJson (include "redpanda.findListenerHostTemplate" (dict "a" (list $state $externalName)))) "r") -}} +{{- if (eq $hostTemplate "") -}} +{{- $hostTemplate = (get (fromJson (include "redpanda.findListenerHost" (dict "a" (list $state $externalName)))) "r") -}} +{{- 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" (dict "name" $externalName "address" $address "port" $port)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.findListenerHostTemplate" -}} +{{- $state := (index .a 0) -}} +{{- $name := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_618_l_6_ok_7 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_6 := (index $_618_l_6_ok_7 0) -}} +{{- $ok_7 := (index $_618_l_6_ok_7 1) -}} +{{- if $ok_7 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_6.hostTemplate "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_622_l_8_ok_9 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_8 := (index $_622_l_8_ok_9 0) -}} +{{- $ok_9 := (index $_622_l_8_ok_9 1) -}} +{{- if $ok_9 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_8.hostTemplate "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_626_l_10_ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_10 := (index $_626_l_10_ok_11 0) -}} +{{- $ok_11 := (index $_626_l_10_ok_11 1) -}} +{{- if $ok_11 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_10.hostTemplate "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_630_l_12_ok_13 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_12 := (index $_630_l_12_ok_13 0) -}} +{{- $ok_13 := (index $_630_l_12_ok_13 1) -}} +{{- if $ok_13 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_12.hostTemplate "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" "") | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.findListenerHost" -}} +{{- $state := (index .a 0) -}} +{{- $name := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_640_l_14_ok_15 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_14 := (index $_640_l_14_ok_15 0) -}} +{{- $ok_15 := (index $_640_l_14_ok_15 1) -}} +{{- if $ok_15 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_14.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_644_l_16_ok_17 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_16 := (index $_644_l_16_ok_17 0) -}} +{{- $ok_17 := (index $_644_l_16_ok_17 1) -}} +{{- if $ok_17 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_16.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_648_l_18_ok_19 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_18 := (index $_648_l_18_ok_19 0) -}} +{{- $ok_19 := (index $_648_l_18_ok_19 1) -}} +{{- if $ok_19 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_18.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_652_l_20_ok_21 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_20 := (index $_652_l_20_ok_21 0) -}} +{{- $ok_21 := (index $_652_l_20_ok_21 1) -}} +{{- if $ok_21 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_20.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" "") | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + {{- define "redpanda.adminInternalHTTPProtocol" -}} {{- $state := (index .a 0) -}} {{- range $_ := (list 1) -}} diff --git a/charts/redpanda/chart/templates/_service.gateway.go.tpl b/charts/redpanda/chart/templates/_service.gateway.go.tpl new file mode 100644 index 000000000..61c157efc --- /dev/null +++ b/charts/redpanda/chart/templates/_service.gateway.go.tpl @@ -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 (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "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 (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "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 (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "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 (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "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 -}} + diff --git a/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl b/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl index 4ed4e8a59..edc0ff8d2 100644 --- a/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl +++ b/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl @@ -10,6 +10,11 @@ {{- (dict "r" (coalesce nil)) | toJson -}} {{- break -}} {{- end -}} +{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} +{{- $_is_returning = true -}} +{{- (dict "r" (coalesce nil)) | toJson -}} +{{- break -}} +{{- end -}} {{- if (ne $state.Values.external.type "LoadBalancer") -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} diff --git a/charts/redpanda/chart/templates/_service.nodeport.go.tpl b/charts/redpanda/chart/templates/_service.nodeport.go.tpl index c32c971bd..1c9747c20 100644 --- a/charts/redpanda/chart/templates/_service.nodeport.go.tpl +++ b/charts/redpanda/chart/templates/_service.nodeport.go.tpl @@ -10,6 +10,11 @@ {{- (dict "r" (coalesce nil)) | toJson -}} {{- break -}} {{- end -}} +{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} +{{- $_is_returning = true -}} +{{- (dict "r" (coalesce nil)) | toJson -}} +{{- break -}} +{{- end -}} {{- if (ne $state.Values.external.type "NodePort") -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} diff --git a/charts/redpanda/chart/templates/_tlsroute.go.tpl b/charts/redpanda/chart/templates/_tlsroute.go.tpl new file mode 100644 index 000000000..bdf3a7753 --- /dev/null +++ b/charts/redpanda/chart/templates/_tlsroute.go.tpl @@ -0,0 +1,144 @@ +{{- /* GENERATED FILE DO NOT EDIT */ -}} +{{- /* Transpiled by gotohelm from "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/tlsroute.go" */ -}} + +{{- define "redpanda.TLSRoutes" -}} +{{- $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 -}} +{{- $gw := $state.Values.external.gateway -}} +{{- $parentRefs := (get (fromJson (include "redpanda.toTLSRouteParentRefs" (dict "a" (list $gw.parentRefs)))) "r") -}} +{{- $labels := (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") -}} +{{- $fullname := (get (fromJson (include "redpanda.Fullname" (dict "a" (list $state)))) "r") -}} +{{- $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 -}} +{{- $routes := (coalesce nil) -}} +{{- range $name, $listener := $state.Values.listeners.kafka.external -}} +{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- continue -}} +{{- end -}} +{{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "kafka" ($listener.port | int))))) "r") -}} +{{- $routes = (concat (default (list) $routes) (default (list) $rs)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $name, $listener := $state.Values.listeners.http.external -}} +{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- continue -}} +{{- end -}} +{{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "http" ($listener.port | int))))) "r") -}} +{{- $routes = (concat (default (list) $routes) (default (list) $rs)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $name, $listener := $state.Values.listeners.admin.external -}} +{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- continue -}} +{{- end -}} +{{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "admin" ($listener.port | int))))) "r") -}} +{{- $routes = (concat (default (list) $routes) (default (list) $rs)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $name, $listener := $state.Values.listeners.schemaRegistry.external -}} +{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- continue -}} +{{- end -}} +{{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "schema" ($listener.port | int))))) "r") -}} +{{- $routes = (concat (default (list) $routes) (default (list) $rs)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $routes) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.tlsRoutesForListener" -}} +{{- $fullname := (index .a 0) -}} +{{- $namespace := (index .a 1) -}} +{{- $labels := (index .a 2) -}} +{{- $parentRefs := (index .a 3) -}} +{{- $pods := (index .a 4) -}} +{{- $host := (index .a 5) -}} +{{- $hostTemplate := (index .a 6) -}} +{{- $name := (index .a 7) -}} +{{- $listenerTag := (index .a 8) -}} +{{- $port := (index .a 9) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $routes := (coalesce nil) -}} +{{- if (eq $host "") -}} +{{- $_is_returning = true -}} +{{- (dict "r" (coalesce nil)) | toJson -}} +{{- break -}} +{{- end -}} +{{- $bootstrapSvcName := (printf "%s-gateway-bootstrap" $fullname) -}} +{{- $bootstrap := (mustMergeOverwrite (dict "metadata" (dict) "spec" (dict "parentRefs" (coalesce nil) "rules" (coalesce nil))) (mustMergeOverwrite (dict) (dict "apiVersion" "gateway.networking.k8s.io/v1alpha2" "kind" "TLSRoute")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (printf "%s-%s-%s-bootstrap" $fullname $listenerTag $name) "namespace" $namespace "labels" $labels)) "spec" (mustMergeOverwrite (dict "parentRefs" (coalesce nil) "rules" (coalesce nil)) (dict "parentRefs" $parentRefs "hostnames" (list $host) "rules" (list (mustMergeOverwrite (dict) (dict "backendRefs" (list (mustMergeOverwrite (dict "name" "" "port" 0) (dict "name" $bootstrapSvcName "port" $port)))))))))) -}} +{{- $routes = (concat (default (list) $routes) (list $bootstrap)) -}} +{{- if (eq $hostTemplate "") -}} +{{- $_is_returning = true -}} +{{- (dict "r" $routes) | toJson -}} +{{- break -}} +{{- end -}} +{{- range $i, $podname := $pods -}} +{{- $brokerHost := (get (fromJson (include "redpanda.renderBrokerHost" (dict "a" (list $hostTemplate $i $podname)))) "r") -}} +{{- $brokerSvcName := (printf "gw-%s" $podname) -}} +{{- $route := (mustMergeOverwrite (dict "metadata" (dict) "spec" (dict "parentRefs" (coalesce nil) "rules" (coalesce nil))) (mustMergeOverwrite (dict) (dict "apiVersion" "gateway.networking.k8s.io/v1alpha2" "kind" "TLSRoute")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (printf "%s-%s-%s-%d" $fullname $listenerTag $name $i) "namespace" $namespace "labels" $labels)) "spec" (mustMergeOverwrite (dict "parentRefs" (coalesce nil) "rules" (coalesce nil)) (dict "parentRefs" $parentRefs "hostnames" (list $brokerHost) "rules" (list (mustMergeOverwrite (dict) (dict "backendRefs" (list (mustMergeOverwrite (dict "name" "" "port" 0) (dict "name" $brokerSvcName "port" $port)))))))))) -}} +{{- $routes = (concat (default (list) $routes) (list $route)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $routes) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.toTLSRouteParentRefs" -}} +{{- $refs := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $parentRefs := (coalesce nil) -}} +{{- range $_, $ref := $refs -}} +{{- $pr := (mustMergeOverwrite (dict "name" "") (dict "name" $ref.name "group" $ref.group "kind" $ref.kind "namespace" $ref.namespace "sectionName" $ref.sectionName)) -}} +{{- $parentRefs = (concat (default (list) $parentRefs) (list $pr)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $parentRefs) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.renderBrokerHost" -}} +{{- $tmpl := (index .a 0) -}} +{{- $ordinal := (index .a 1) -}} +{{- $podName := (index .a 2) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $result := (replace "$POD_ORDINAL" (printf "%d" $ordinal) $tmpl) -}} +{{- $result = (replace "$POD_NAME" $podName $result) -}} +{{- $_is_returning = true -}} +{{- (dict "r" $result) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + diff --git a/charts/redpanda/chart/templates/_values.go.tpl b/charts/redpanda/chart/templates/_values.go.tpl index 818f2a1a6..395cf5a69 100644 --- a/charts/redpanda/chart/templates/_values.go.tpl +++ b/charts/redpanda/chart/templates/_values.go.tpl @@ -88,6 +88,31 @@ {{- end -}} {{- end -}} +{{- define "redpanda.ExternalConfig.IsGatewayEnabled" -}} +{{- $e := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (and (and (and $e.enabled (ne (toJson $e.gateway) "null")) $e.gateway.enabled) (gt ((get (fromJson (include "_shims.len" (dict "a" (list $e.gateway.parentRefs)))) "r") | int) (0 | int)))) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.GatewayConfig.GatewayAdvertisedPort" -}} +{{- $g := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- if (ne (toJson $g.advertisedPort) "null") -}} +{{- $_is_returning = true -}} +{{- (dict "r" $g.advertisedPort) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" (443 | int)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + {{- define "redpanda.Logging.Translate" -}} {{- $l := (index .a 0) -}} {{- range $_ := (list 1) -}} @@ -152,13 +177,13 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} -{{- $_436_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0")))) "r") -}} -{{- $cpuReq := (index $_436_cpuReq_ok 0) -}} -{{- $ok := (index $_436_cpuReq_ok 1) -}} +{{- $_491_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0")))) "r") -}} +{{- $cpuReq := (index $_491_cpuReq_ok 0) -}} +{{- $ok := (index $_491_cpuReq_ok 1) -}} {{- if (not $ok) -}} -{{- $_438_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0")))) "r") -}} -{{- $cpuReq = (index $_438_cpuReq_ok 0) -}} -{{- $ok = (index $_438_cpuReq_ok 1) -}} +{{- $_493_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0")))) "r") -}} +{{- $cpuReq = (index $_493_cpuReq_ok 0) -}} +{{- $ok = (index $_493_cpuReq_ok 1) -}} {{- end -}} {{- if (and $ok (lt ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $cpuReq)))) "r") | int64) (1000 | int64))) -}} {{- $_is_returning = true -}} @@ -185,13 +210,13 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} -{{- $_462_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0")))) "r") -}} -{{- $cpuReq := (index $_462_cpuReq_ok 0) -}} -{{- $ok := (index $_462_cpuReq_ok 1) -}} +{{- $_517_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0")))) "r") -}} +{{- $cpuReq := (index $_517_cpuReq_ok 0) -}} +{{- $ok := (index $_517_cpuReq_ok 1) -}} {{- if (not $ok) -}} -{{- $_464_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0")))) "r") -}} -{{- $cpuReq = (index $_464_cpuReq_ok 0) -}} -{{- $ok = (index $_464_cpuReq_ok 1) -}} +{{- $_519_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0")))) "r") -}} +{{- $cpuReq = (index $_519_cpuReq_ok 0) -}} +{{- $ok = (index $_519_cpuReq_ok 1) -}} {{- end -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} @@ -223,13 +248,13 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} -{{- $_521_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "memory" "0")))) "r") -}} -{{- $memReq := (index $_521_memReq_ok 0) -}} -{{- $ok := (index $_521_memReq_ok 1) -}} +{{- $_576_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "memory" "0")))) "r") -}} +{{- $memReq := (index $_576_memReq_ok 0) -}} +{{- $ok := (index $_576_memReq_ok 1) -}} {{- if (not $ok) -}} -{{- $_523_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "memory" "0")))) "r") -}} -{{- $memReq = (index $_523_memReq_ok 0) -}} -{{- $ok = (index $_523_memReq_ok 1) -}} +{{- $_578_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "memory" "0")))) "r") -}} +{{- $memReq = (index $_578_memReq_ok 0) -}} +{{- $ok = (index $_578_memReq_ok 1) -}} {{- end -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} @@ -305,9 +330,9 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- $conf := (get (fromJson (include "redpanda.Storage.GetTieredStorageConfig" (dict "a" (list $s)))) "r") -}} -{{- $_641_b_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $conf "cloud_storage_enabled" (coalesce nil))))) "r") -}} -{{- $b := (index $_641_b_ok 0) -}} -{{- $ok := (index $_641_b_ok 1) -}} +{{- $_696_b_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $conf "cloud_storage_enabled" (coalesce nil))))) "r") -}} +{{- $b := (index $_696_b_ok 0) -}} +{{- $ok := (index $_696_b_ok 1) -}} {{- $_is_returning = true -}} {{- (dict "r" (and $ok (get (fromJson (include "_shims.typeassertion" (dict "a" (list "bool" $b)))) "r"))) | toJson -}} {{- break -}} @@ -351,18 +376,18 @@ {{- $state := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_669_dir_7_ok_8 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $state.Values.config.node "cloud_storage_cache_directory") "")))) "r") -}} -{{- $dir_7 := (index $_669_dir_7_ok_8 0) -}} -{{- $ok_8 := (index $_669_dir_7_ok_8 1) -}} +{{- $_724_dir_7_ok_8 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $state.Values.config.node "cloud_storage_cache_directory") "")))) "r") -}} +{{- $dir_7 := (index $_724_dir_7_ok_8 0) -}} +{{- $ok_8 := (index $_724_dir_7_ok_8 1) -}} {{- if $ok_8 -}} {{- $_is_returning = true -}} {{- (dict "r" $dir_7) | toJson -}} {{- break -}} {{- end -}} {{- $tieredConfig := (get (fromJson (include "redpanda.Storage.GetTieredStorageConfig" (dict "a" (list $state.Values.storage)))) "r") -}} -{{- $_678_dir_9_ok_10 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $tieredConfig "cloud_storage_cache_directory") "")))) "r") -}} -{{- $dir_9 := (index $_678_dir_9_ok_10 0) -}} -{{- $ok_10 := (index $_678_dir_9_ok_10 1) -}} +{{- $_733_dir_9_ok_10 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $tieredConfig "cloud_storage_cache_directory") "")))) "r") -}} +{{- $dir_9 := (index $_733_dir_9_ok_10 0) -}} +{{- $ok_10 := (index $_733_dir_9_ok_10 1) -}} {{- if $ok_10 -}} {{- $_is_returning = true -}} {{- (dict "r" $dir_9) | toJson -}} @@ -477,9 +502,9 @@ {{- $result := (dict) -}} {{- $s := (toJson $t) -}} {{- $tune := (fromJson $s) -}} -{{- $_838_m_ok := (get (fromJson (include "_shims.typetest" (dict "a" (list (printf "map[%s]%s" "string" "interface {}") $tune (coalesce nil))))) "r") -}} -{{- $m := (index $_838_m_ok 0) -}} -{{- $ok := (index $_838_m_ok 1) -}} +{{- $_893_m_ok := (get (fromJson (include "_shims.typetest" (dict "a" (list (printf "map[%s]%s" "string" "interface {}") $tune (coalesce nil))))) "r") -}} +{{- $m := (index $_893_m_ok 0) -}} +{{- $ok := (index $_893_m_ok 1) -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} {{- (dict "r" (dict)) | toJson -}} @@ -626,9 +651,9 @@ {{- $seen := (dict) -}} {{- $deduped := (coalesce nil) -}} {{- range $_, $item := $items -}} -{{- $_1027___ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $seen $item.key false)))) "r") -}} -{{- $_ := (index $_1027___ok_11 0) -}} -{{- $ok_11 := (index $_1027___ok_11 1) -}} +{{- $_1082___ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $seen $item.key false)))) "r") -}} +{{- $_ := (index $_1082___ok_11 0) -}} +{{- $ok_11 := (index $_1082___ok_11 1) -}} {{- if $ok_11 -}} {{- continue -}} {{- end -}} @@ -851,9 +876,9 @@ {{- $name := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1315_cert_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $m $name (dict "enabled" (coalesce nil) "caEnabled" false "applyInternalDNSNames" (coalesce nil) "duration" "" "issuerRef" (coalesce nil) "secretRef" (coalesce nil) "clientSecretRef" (coalesce nil)))))) "r") -}} -{{- $cert := (index $_1315_cert_ok 0) -}} -{{- $ok := (index $_1315_cert_ok 1) -}} +{{- $_1370_cert_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $m $name (dict "enabled" (coalesce nil) "caEnabled" false "applyInternalDNSNames" (coalesce nil) "duration" "" "issuerRef" (coalesce nil) "secretRef" (coalesce nil) "clientSecretRef" (coalesce nil)))))) "r") -}} +{{- $cert := (index $_1370_cert_ok 0) -}} +{{- $ok := (index $_1370_cert_ok 1) -}} {{- if (not $ok) -}} {{- $_ := (fail (printf "Certificate %q referenced, but not found in the tls.certs map" $name)) -}} {{- end -}} @@ -1286,7 +1311,7 @@ {{- $auth = $authAStr -}} {{- end -}} {{- $_is_returning = true -}} -{{- (dict "r" (mustMergeOverwrite (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)) (dict "enabled" $l.enabled "advertisedPorts" $l.advertisedPorts "port" ($l.port | int) "nodePort" $l.nodePort "tls" $l.tls "authenticationMethod" $auth "prefixTemplate" $l.prefixTemplate))) | toJson -}} +{{- (dict "r" (mustMergeOverwrite (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)) (dict "enabled" $l.enabled "advertisedPorts" $l.advertisedPorts "port" ($l.port | int) "nodePort" $l.nodePort "tls" $l.tls "authenticationMethod" $auth "prefixTemplate" $l.prefixTemplate "host" $l.host "hostTemplate" $l.hostTemplate))) | toJson -}} {{- break -}} {{- end -}} {{- end -}} @@ -1332,9 +1357,9 @@ {{- $result := (dict) -}} {{- range $k, $v := $c -}} {{- if (not (empty $v)) -}} -{{- $_1845___ok_15 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v)))) "r") -}} -{{- $_ := ((index $_1845___ok_15 0) | float64) -}} -{{- $ok_15 := (index $_1845___ok_15 1) -}} +{{- $_1911___ok_15 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v)))) "r") -}} +{{- $_ := ((index $_1911___ok_15 0) | float64) -}} +{{- $ok_15 := (index $_1911___ok_15 1) -}} {{- if $ok_15 -}} {{- $_ := (set $result $k $v) -}} {{- else -}}{{- if (kindIs "bool" $v) -}} @@ -1360,9 +1385,9 @@ {{- $_is_returning := false -}} {{- $result := (dict) -}} {{- range $k, $v := $c -}} -{{- $_1865_b_16_ok_17 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false)))) "r") -}} -{{- $b_16 := (index $_1865_b_16_ok_17 0) -}} -{{- $ok_17 := (index $_1865_b_16_ok_17 1) -}} +{{- $_1931_b_16_ok_17 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false)))) "r") -}} +{{- $b_16 := (index $_1931_b_16_ok_17 0) -}} +{{- $ok_17 := (index $_1931_b_16_ok_17 1) -}} {{- if $ok_17 -}} {{- $_ := (set $result $k $b_16) -}} {{- continue -}} @@ -1405,15 +1430,15 @@ {{- $config := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1910___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1910___hasAccessKey 0) -}} -{{- $hasAccessKey := (index $_1910___hasAccessKey 1) -}} -{{- $_1911___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1911___hasSecretKey 0) -}} -{{- $hasSecretKey := (index $_1911___hasSecretKey 1) -}} -{{- $_1912___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1912___hasSharedKey 0) -}} -{{- $hasSharedKey := (index $_1912___hasSharedKey 1) -}} +{{- $_1976___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1976___hasAccessKey 0) -}} +{{- $hasAccessKey := (index $_1976___hasAccessKey 1) -}} +{{- $_1977___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1977___hasSecretKey 0) -}} +{{- $hasSecretKey := (index $_1977___hasSecretKey 1) -}} +{{- $_1978___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1978___hasSharedKey 0) -}} +{{- $hasSharedKey := (index $_1978___hasSharedKey 1) -}} {{- $envvars := (coalesce nil) -}} {{- if (and (not $hasAccessKey) (get (fromJson (include "redpanda.SecretRef.IsValid" (dict "a" (list $tsc.accessKey)))) "r")) -}} {{- $envvars = (concat (default (list) $envvars) (list (mustMergeOverwrite (dict "name" "") (dict "name" "REDPANDA_CLOUD_STORAGE_ACCESS_KEY" "valueFrom" (get (fromJson (include "redpanda.SecretRef.AsSource" (dict "a" (list $tsc.accessKey)))) "r"))))) -}} @@ -1436,12 +1461,12 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1948___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1948___containerExists 0) -}} -{{- $containerExists := (index $_1948___containerExists 1) -}} -{{- $_1949___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1949___accountExists 0) -}} -{{- $accountExists := (index $_1949___accountExists 1) -}} +{{- $_2014___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil))))) "r") -}} +{{- $_ := (index $_2014___containerExists 0) -}} +{{- $containerExists := (index $_2014___containerExists 1) -}} +{{- $_2015___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil))))) "r") -}} +{{- $_ := (index $_2015___accountExists 0) -}} +{{- $accountExists := (index $_2015___accountExists 1) -}} {{- $_is_returning = true -}} {{- (dict "r" (and $containerExists $accountExists)) | toJson -}} {{- break -}} @@ -1452,9 +1477,9 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1954_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil))))) "r") -}} -{{- $value := (index $_1954_value_ok 0) -}} -{{- $ok := (index $_1954_value_ok 1) -}} +{{- $_2020_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil))))) "r") -}} +{{- $value := (index $_2020_value_ok 0) -}} +{{- $ok := (index $_2020_value_ok 1) -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} diff --git a/charts/redpanda/chart/values.schema.json b/charts/redpanda/chart/values.schema.json index 64e0b0ce1..5d2dc109e 100644 --- a/charts/redpanda/chart/values.schema.json +++ b/charts/redpanda/chart/values.schema.json @@ -4526,6 +4526,49 @@ ], "type": "object" }, + "gateway": { + "additionalProperties": false, + "properties": { + "advertisedPort": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "parentRefs": { + "oneOf": [ + { + "items": { + "additionalProperties": false, + "properties": { + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "sectionName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, "prefixTemplate": { "type": "string" }, @@ -4644,6 +4687,12 @@ "enabled": { "type": "boolean" }, + "host": { + "type": "string" + }, + "hostTemplate": { + "type": "string" + }, "nodePort": { "type": "integer" }, @@ -4835,6 +4884,12 @@ "enabled": { "type": "boolean" }, + "host": { + "type": "string" + }, + "hostTemplate": { + "type": "string" + }, "nodePort": { "type": "integer" }, @@ -5028,6 +5083,12 @@ "enabled": { "type": "boolean" }, + "host": { + "type": "string" + }, + "hostTemplate": { + "type": "string" + }, "nodePort": { "type": "integer" }, @@ -5281,6 +5342,12 @@ "enabled": { "type": "boolean" }, + "host": { + "type": "string" + }, + "hostTemplate": { + "type": "string" + }, "nodePort": { "type": "integer" }, diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index e97ce7546..707b22ef1 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -106454,6 +106454,1658 @@ spec: # Source: redpanda/templates/entry-point.yaml apiVersion: batch/v1 kind: Job +metadata: + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-delete-policy: before-hook-creation + helm.sh/hook-weight: "-5" + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-configuration + namespace: default +spec: + template: + metadata: + annotations: {} + generateName: redpanda-post- + labels: + app.kubernetes.io/component: redpanda-post-install + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda-configuration + spec: + automountServiceAccountToken: false + containers: + - command: + - /redpanda-operator + - sync-cluster-config + - --users-directory + - /etc/secrets/users + - --redpanda-yaml + - /tmp/base-config/redpanda.yaml + - --bootstrap-yaml + - /tmp/config/.bootstrap.yaml + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: post-install + resources: {} + securityContext: {} + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /tmp/config + name: config + - mountPath: /tmp/base-config + name: base-config + imagePullSecrets: [] + initContainers: + - command: + - /redpanda-operator + - bootstrap + - --in-dir + - /tmp/base-config + - --out-dir + - /tmp/config + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: bootstrap-yaml-envsubst + resources: + limits: + cpu: 100m + memory: 125Mi + requests: + cpu: 100m + memory: 125Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /tmp/config/ + name: config + - mountPath: /tmp/base-config/ + name: base-config + nodeSelector: {} + restartPolicy: Never + securityContext: + fsGroup: 101 + fsGroupChangePolicy: OnRootMismatch + runAsUser: 101 + serviceAccountName: redpanda + tolerations: [] + volumes: + - name: redpanda-default-cert + secret: + defaultMode: 288 + secretName: redpanda-default-cert + - name: redpanda-external-cert + secret: + defaultMode: 288 + secretName: redpanda-external-cert + - configMap: + name: redpanda + name: base-config + - emptyDir: {} + name: config +-- testdata/TestTemplate/gateway-api-tlsroute.yaml.golden -- +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +spec: + maxUnavailable: 1 + selector: + matchLabels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + redpanda.com/poddisruptionbudget: redpanda +--- +# Source: redpanda/charts/console/templates/entry-point.yaml +apiVersion: v1 +automountServiceAccountToken: false +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +automountServiceAccountToken: false +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sts-lifecycle + namespace: default +stringData: + common.sh: |- + #!/usr/bin/env bash + + # the SERVICE_NAME comes from the metadata.name of the pod, essentially the POD_NAME + CURL_URL="https://${SERVICE_NAME}.redpanda.default.svc.cluster.local:9644" + + # commands used throughout + CURL_NODE_ID_CMD="curl --silent --fail --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/node_config" + + CURL_MAINTENANCE_DELETE_CMD_PREFIX='curl -X DELETE --silent -o /dev/null -w "%{http_code}"' + CURL_MAINTENANCE_PUT_CMD_PREFIX='curl -X PUT --silent -o /dev/null -w "%{http_code}"' + CURL_MAINTENANCE_GET_CMD="curl -X GET --silent --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/maintenance" + postStart.sh: |- + #!/usr/bin/env bash + # This code should be similar if not exactly the same as that found in the panda-operator, see + # https://github.com/redpanda-data/redpanda/blob/e51d5b7f2ef76d5160ca01b8c7a8cf07593d29b6/src/go/k8s/pkg/resources/secret.go + + # path below should match the path defined on the statefulset + source /var/lifecycle/common.sh + + postStartHook () { + set -x + + touch /tmp/postStartHookStarted + + until NODE_ID=$(${CURL_NODE_ID_CMD} | grep -o '\"node_id\":[^,}]*' | grep -o '[^: ]*$'); do + sleep 0.5 + done + + echo "Clearing maintenance mode on node ${NODE_ID}" + CURL_MAINTENANCE_DELETE_CMD="${CURL_MAINTENANCE_DELETE_CMD_PREFIX} --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/brokers/${NODE_ID}/maintenance" + # a 400 here would mean not in maintenance mode + until [ "${status:-}" = '"200"' ] || [ "${status:-}" = '"400"' ]; do + status=$(${CURL_MAINTENANCE_DELETE_CMD}) + sleep 0.5 + done + + touch /tmp/postStartHookFinished + } + + postStartHook + true + preStop.sh: |- + #!/usr/bin/env bash + # This code should be similar if not exactly the same as that found in the panda-operator, see + # https://github.com/redpanda-data/redpanda/blob/e51d5b7f2ef76d5160ca01b8c7a8cf07593d29b6/src/go/k8s/pkg/resources/secret.go + + touch /tmp/preStopHookStarted + + # path below should match the path defined on the statefulset + source /var/lifecycle/common.sh + + set -x + + preStopHook () { + until NODE_ID=$(${CURL_NODE_ID_CMD} | grep -o '\"node_id\":[^,}]*' | grep -o '[^: ]*$'); do + sleep 0.5 + done + + echo "Setting maintenance mode on node ${NODE_ID}" + CURL_MAINTENANCE_PUT_CMD="${CURL_MAINTENANCE_PUT_CMD_PREFIX} --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/brokers/${NODE_ID}/maintenance" + until [ "${status:-}" = '"200"' ]; do + status=$(${CURL_MAINTENANCE_PUT_CMD}) + sleep 0.5 + done + + until [ "${finished:-}" = "true" ] || [ "${draining:-}" = "false" ]; do + res=$(${CURL_MAINTENANCE_GET_CMD}) + finished=$(echo $res | grep -o '\"finished\":[^,}]*' | grep -o '[^: ]*$') + draining=$(echo $res | grep -o '\"draining\":[^,}]*' | grep -o '[^: ]*$') + sleep 0.5 + done + + touch /tmp/preStopHookFinished + } + preStopHook + true +type: Opaque +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-configurator + namespace: default +stringData: + configurator.sh: |- + set -xe + SERVICE_NAME=$1 + KUBERNETES_NODE_NAME=$2 + POD_ORDINAL=${SERVICE_NAME##*-} + BROKER_INDEX=`expr $POD_ORDINAL + 1` + + CONFIG=/etc/redpanda/redpanda.yaml + + # Setup config files + cp /tmp/base-config/redpanda.yaml "${CONFIG}" + + LISTENER="{\"address\":\"${SERVICE_NAME}.redpanda.default.svc.cluster.local.\",\"name\":\"internal\",\"port\":9093}" + rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[0] "$LISTENER" + + ADVERTISED_KAFKA_ADDRESSES=() + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[1] "${ADVERTISED_KAFKA_ADDRESSES[$POD_ORDINAL]}" + + LISTENER="{\"address\":\"${SERVICE_NAME}.redpanda.default.svc.cluster.local.\",\"name\":\"internal\",\"port\":8082}" + rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[0] "$LISTENER" + + ADVERTISED_HTTP_ADDRESSES=() + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") + + rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[1] "${ADVERTISED_HTTP_ADDRESSES[$POD_ORDINAL]}" +type: Opaque +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + .bootstrap.json.in: '{"audit_enabled":"false","cloud_storage_cache_size":"5368709120","cloud_storage_enable_remote_read":"true","cloud_storage_enable_remote_write":"true","cloud_storage_enabled":"false","compacted_log_segment_size":"67108864","default_topic_replications":"3","enable_rack_awareness":"false","enable_sasl":"false","kafka_connection_rate_limit":"1000","kafka_enable_authorization":"false","log_segment_size_max":"268435456","log_segment_size_min":"16777216","max_compacted_log_segment_size":"536870912","storage_min_free_bytes":"1073741824"}' + bootstrap.yaml.fixups: '[]' + redpanda.yaml: |- + config_file: /etc/redpanda/redpanda.yaml + pandaproxy: + pandaproxy_api: + - address: 0.0.0.0 + name: internal + port: 8082 + - address: 0.0.0.0 + name: default + port: 8083 + pandaproxy_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + pandaproxy_client: + broker_tls: + enabled: true + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + brokers: + - address: redpanda-0.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-1.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-2.redpanda.default.svc.cluster.local. + port: 9093 + redpanda: + admin: + - address: 0.0.0.0 + name: internal + port: 9644 + - address: 0.0.0.0 + name: default + port: 9645 + admin_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + crash_loop_limit: 5 + empty_seed_starts_cluster: false + kafka_api: + - address: 0.0.0.0 + name: internal + port: 9093 + - address: 0.0.0.0 + name: default + port: 9094 + kafka_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + rpc_server: + address: 0.0.0.0 + port: 33145 + rpc_server_tls: + cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + seed_servers: + - host: + address: redpanda-0.redpanda.default.svc.cluster.local. + port: 33145 + - host: + address: redpanda-1.redpanda.default.svc.cluster.local. + port: 33145 + - host: + address: redpanda-2.redpanda.default.svc.cluster.local. + port: 33145 + rpk: + additional_start_flags: + - --default-log-level=info + - --memory=2048M + - --reserve-memory=205M + - --smp=1 + admin_api: + addresses: + - redpanda-0.redpanda.default.svc.cluster.local.:9644 + - redpanda-1.redpanda.default.svc.cluster.local.:9644 + - redpanda-2.redpanda.default.svc.cluster.local.:9644 + tls: + ca_file: /etc/tls/certs/default/ca.crt + enable_memory_locking: false + kafka_api: + brokers: + - redpanda-0.redpanda.default.svc.cluster.local.:9093 + - redpanda-1.redpanda.default.svc.cluster.local.:9093 + - redpanda-2.redpanda.default.svc.cluster.local.:9093 + tls: + ca_file: /etc/tls/certs/default/ca.crt + overprovisioned: false + schema_registry: + addresses: + - redpanda-0.redpanda.default.svc.cluster.local.:8081 + - redpanda-1.redpanda.default.svc.cluster.local.:8081 + - redpanda-2.redpanda.default.svc.cluster.local.:8081 + tls: + ca_file: /etc/tls/certs/default/ca.crt + tune_aio_events: true + schema_registry: + schema_registry_api: + - address: 0.0.0.0 + name: internal + port: 8081 + - address: 0.0.0.0 + name: default + port: 8084 + schema_registry_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + schema_registry_client: + broker_tls: + enabled: true + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + brokers: + - address: redpanda-0.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-1.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-2.redpanda.default.svc.cluster.local. + port: 9093 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + profile: |- + admin_api: + addresses: + - redpanda-0:31644 + - redpanda-1:31644 + - redpanda-2:31644 + tls: + ca_file: ca.crt + kafka_api: + brokers: + - redpanda-0:31092 + - redpanda-1:31092 + - redpanda-2:31092 + tls: + ca_file: ca.crt + name: default + schema_registry: + addresses: + - redpanda-0:30081 + - redpanda-1:30081 + - redpanda-2:30081 + tls: + ca_file: ca.crt +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + config.yaml: | + # from .Values.config + kafka: + brokers: + - redpanda-0.redpanda.default.svc.cluster.local.:9093 + - redpanda-1.redpanda.default.svc.cluster.local.:9093 + - redpanda-2.redpanda.default.svc.cluster.local.:9093 + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + redpanda: + adminApi: + enabled: true + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + urls: + - https://redpanda.default.svc.cluster.local.:9644 + schemaRegistry: + enabled: true + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + urls: + - https://redpanda-0.redpanda.default.svc.cluster.local.:8081 + - https://redpanda-1.redpanda.default.svc.cluster.local.:8081 + - https://redpanda-2.redpanda.default.svc.cluster.local.:8081 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk-debug-bundle + namespace: default +rules: +- apiGroups: + - "" + resources: + - configmaps + - endpoints + - events + - limitranges + - persistentvolumeclaims + - pods + - pods/log + - replicationcontrollers + - resourcequotas + - serviceaccounts + - services + verbs: + - get + - list +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sidecar + namespace: default +rules: +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk-debug-bundle + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: redpanda-rpk-debug-bundle +subjects: +- kind: ServiceAccount + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sidecar + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: redpanda-sidecar +subjects: +- kind: ServiceAccount + name: redpanda + namespace: default +--- +# Source: redpanda/charts/console/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 0 + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + monitoring.redpanda.com/enabled: "false" + name: redpanda + namespace: default +spec: + clusterIP: None + ports: + - appProtocol: null + name: admin + port: 9644 + protocol: TCP + targetPort: 9644 + - name: http + port: 8082 + protocol: TCP + targetPort: 8082 + - name: kafka + port: 9093 + protocol: TCP + targetPort: 9093 + - name: rpc + port: 33145 + protocol: TCP + targetPort: 33145 + - name: schemaregistry + port: 8081 + protocol: TCP + targetPort: 8081 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-gateway-bootstrap + namespace: default +spec: + ports: + - name: admin-default + port: 9645 + protocol: TCP + targetPort: 0 + - name: kafka-default + port: 9094 + protocol: TCP + targetPort: 0 + - name: http-default + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-0 + namespace: default +spec: + ports: + - name: admin-default + port: 9645 + protocol: TCP + targetPort: 0 + - name: kafka-default + port: 9094 + protocol: TCP + targetPort: 0 + - name: http-default + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-0 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-1 + namespace: default +spec: + ports: + - name: admin-default + port: 9645 + protocol: TCP + targetPort: 0 + - name: kafka-default + port: 9094 + protocol: TCP + targetPort: 0 + - name: http-default + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-1 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-2 + namespace: default +spec: + ports: + - name: admin-default + port: 9645 + protocol: TCP + targetPort: 0 + - name: kafka-default + port: 9094 + protocol: TCP + targetPort: 0 + - name: http-default + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-2 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + strategy: {} + template: + metadata: + annotations: + checksum/config: 44e632405e10e419e4cb3a5f69d2911edabaa8fd561fc25ec1017dc35a99fc96 + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + spec: + affinity: {} + automountServiceAccountToken: false + containers: + - args: + - --config.filepath=/etc/console/configs/config.yaml + command: null + env: + - name: REDPANDA_METRICS_K8S_DEPLOYMENT_TYPE + value: helm + - name: REDPANDA_METRICS_K8S_CHART_VERSION + value: 3.7.0 + - name: REDPANDA_METRICS_K8S_CONSOLE_IMAGE_VERSION + value: redpandadata/console:v3.7.0 + - name: REDPANDA_METRICS_K8S_VERSION + value: v1.99.0-gke + - name: REDPANDA_METRICS_K8S_ENVIRONMENT + value: GCP + envFrom: [] + image: docker.redpanda.com/redpandadata/console:v3.7.0 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /admin/health + port: http + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: console + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /admin/health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/console/configs + name: configs + readOnly: true + - mountPath: /etc/tls/certs + name: redpanda-certificates + imagePullSecrets: [] + initContainers: null + nodeSelector: {} + priorityClassName: "" + securityContext: + fsGroup: 99 + fsGroupChangePolicy: Always + runAsUser: 99 + serviceAccountName: redpanda-console + tolerations: [] + topologySpreadConstraints: [] + volumes: + - configMap: + name: redpanda-console + name: configs + - name: redpanda-certificates + projected: + sources: + - secret: + items: + - key: ca.crt + path: secrets/redpanda-default-cert/ca.crt + name: redpanda-default-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +spec: + podManagementPolicy: Parallel + replicas: 3 + selector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + serviceName: redpanda + template: + metadata: + annotations: + config.redpanda.com/checksum: e5b4f6eb32c0dcd720400c66c2ed67133071f6fa09f483e8049af4b845a6d9a1 + labels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + cluster.redpanda.com/broker: "true" + helm.sh/chart: redpanda-26.1.1 + redpanda.com/poddisruptionbudget: redpanda + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + topologyKey: kubernetes.io/hostname + automountServiceAccountToken: false + containers: + - command: + - rpk + - redpanda + - start + - --advertise-rpc-addr=$(SERVICE_NAME).redpanda.default.svc.cluster.local.:33145 + env: + - name: SERVICE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: REDPANDA_METRICS_K8S_VERSION + value: v1.99.0-gke + - name: REDPANDA_METRICS_K8S_DEPLOYMENT_TYPE + value: helm + - name: REDPANDA_METRICS_K8S_CHART_VERSION + value: 26.1.1 + - name: REDPANDA_METRICS_K8S_OPERATOR_IMAGE_VERSION + value: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + - name: REDPANDA_METRICS_K8S_ENVIRONMENT + value: GCP + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + lifecycle: + postStart: + exec: + command: + - bash + - -c + - 'timeout -v 45 bash -x /var/lifecycle/postStart.sh 2>&1 | sed "s/^/lifecycle-hook + post-start $(date): /" | tee /proc/1/fd/1; true' + preStop: + exec: + command: + - bash + - -c + - 'timeout -v 45 bash -x /var/lifecycle/preStop.sh 2>&1 | sed "s/^/lifecycle-hook + pre-stop $(date): /" | tee /proc/1/fd/1; true' + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 9644 + name: redpanda + ports: + - containerPort: 9644 + name: admin + - containerPort: 9645 + name: admin-default + - containerPort: 8082 + name: http + - containerPort: 8083 + name: http-default + - containerPort: 9093 + name: kafka + - containerPort: 9094 + name: kafka-default + - containerPort: 33145 + name: rpc + - containerPort: 8081 + name: schemaregistry + - containerPort: 8084 + name: schema-default + resources: + limits: + cpu: 1 + memory: 2.5Gi + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + startupProbe: + exec: + command: + - /bin/sh + - -c + - | + set -e + RESULT=$(curl --silent --fail -k -m 5 --cacert /etc/tls/certs/default/ca.crt "https://${SERVICE_NAME}.redpanda.default.svc.cluster.local.:9644/v1/status/ready") + echo $RESULT + echo $RESULT | grep ready + failureThreshold: 120 + initialDelaySeconds: 1 + periodSeconds: 10 + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /tmp/base-config + name: base-config + - mountPath: /var/lifecycle + name: lifecycle-scripts + - mountPath: /var/lib/redpanda/data + name: datadir + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + - args: + - supervisor + - -- + - /redpanda-operator + - sidecar + - --redpanda-yaml + - /etc/redpanda/redpanda.yaml + - --redpanda-cluster-namespace + - default + - --redpanda-cluster-name + - redpanda + - --selector=helm.sh/chart=redpanda-26.1.1,app.kubernetes.io/name=redpanda,app.kubernetes.io/instance=redpanda + - --run-broker-probe + - --broker-probe-broker-url + - $(SERVICE_NAME).redpanda.default.svc.cluster.local.:9644 + command: + - /redpanda-operator + env: + - name: SERVICE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: sidecar + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 8093 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 0 + resources: {} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + imagePullSecrets: [] + initContainers: + - command: + - /bin/bash + - -c + - rpk redpanda tune all + env: null + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + name: tuning + resources: {} + securityContext: + capabilities: + add: + - SYS_RESOURCE + privileged: true + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: base-config + - mountPath: /var/lib/redpanda/data + name: datadir + - command: + - /bin/bash + - -c + - trap "exit 0" TERM; exec $CONFIGURATOR_SCRIPT "${SERVICE_NAME}" "${KUBERNETES_NODE_NAME}" + & wait $! + env: + - name: CONFIGURATOR_SCRIPT + value: /etc/secrets/configurator/scripts/configurator.sh + - name: SERVICE_NAME + valueFrom: + configMapKeyRef: null + fieldRef: + fieldPath: metadata.name + resourceFieldRef: null + secretKeyRef: null + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: HOST_IP_ADDRESS + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.hostIP + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + name: redpanda-configurator + resources: {} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /tmp/base-config + name: base-config + - mountPath: /etc/secrets/configurator/scripts/ + name: redpanda-configurator + - command: + - /redpanda-operator + - bootstrap + - --in-dir + - /tmp/base-config + - --out-dir + - /tmp/config + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: bootstrap-yaml-envsubst + resources: + limits: + cpu: 100m + memory: 125Mi + requests: + cpu: 100m + memory: 125Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /tmp/config/ + name: config + - mountPath: /tmp/base-config/ + name: base-config + nodeSelector: {} + priorityClassName: "" + securityContext: + fsGroup: 101 + fsGroupChangePolicy: OnRootMismatch + runAsUser: 101 + serviceAccountName: redpanda + terminationGracePeriodSeconds: 90 + tolerations: [] + topologySpreadConstraints: + - labelSelector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + volumes: + - name: redpanda-default-cert + secret: + defaultMode: 288 + secretName: redpanda-default-cert + - name: redpanda-external-cert + secret: + defaultMode: 288 + secretName: redpanda-external-cert + - name: lifecycle-scripts + secret: + defaultMode: 509 + secretName: redpanda-sts-lifecycle + - configMap: + name: redpanda + name: base-config + - emptyDir: {} + name: config + - name: redpanda-configurator + secret: + defaultMode: 509 + secretName: redpanda-configurator + - name: datadir + persistentVolumeClaim: + claimName: datadir + - name: kube-api-access + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: + - metadata: + annotations: null + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + name: datadir + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + status: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-root-certificate + namespace: default +spec: + commonName: redpanda-default-root-certificate + duration: 43800h0m0s + isCA: true + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-default-selfsigned-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-default-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-root-certificate + namespace: default +spec: + commonName: redpanda-external-root-certificate + duration: 43800h0m0s + isCA: true + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-external-selfsigned-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-external-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-cert + namespace: default +spec: + dnsNames: + - redpanda-cluster.redpanda.default.svc.cluster.local + - redpanda-cluster.redpanda.default.svc + - redpanda-cluster.redpanda.default + - '*.redpanda-cluster.redpanda.default.svc.cluster.local' + - '*.redpanda-cluster.redpanda.default.svc' + - '*.redpanda-cluster.redpanda.default' + - redpanda.default.svc.cluster.local + - redpanda.default.svc + - redpanda.default + - '*.redpanda.default.svc.cluster.local' + - '*.redpanda.default.svc' + - '*.redpanda.default' + duration: 43800h0m0s + isCA: false + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-default-root-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-default-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-cert + namespace: default +spec: + dnsNames: + - redpanda-cluster.redpanda.default.svc.cluster.local + - redpanda-cluster.redpanda.default.svc + - redpanda-cluster.redpanda.default + - '*.redpanda-cluster.redpanda.default.svc.cluster.local' + - '*.redpanda-cluster.redpanda.default.svc' + - '*.redpanda-cluster.redpanda.default' + - redpanda.default.svc.cluster.local + - redpanda.default.svc + - redpanda.default + - '*.redpanda.default.svc.cluster.local' + - '*.redpanda.default.svc' + - '*.redpanda.default' + duration: 43800h0m0s + isCA: false + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-external-root-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-external-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-selfsigned-issuer + namespace: default +spec: + selfSigned: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-root-issuer + namespace: default +spec: + ca: + secretName: redpanda-default-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-selfsigned-issuer + namespace: default +spec: + selfSigned: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-root-issuer + namespace: default +spec: + ca: + secretName: redpanda-external-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-default-bootstrap + namespace: default +spec: + hostnames: + - kafka.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: redpanda-gateway-bootstrap + port: 9094 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-default-0 + namespace: default +spec: + hostnames: + - kafka-0-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-0 + port: 9094 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-default-1 + namespace: default +spec: + hostnames: + - kafka-1-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-1 + port: 9094 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-default-2 + namespace: default +spec: + hostnames: + - kafka-2-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-2 + port: 9094 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: batch/v1 +kind: Job metadata: annotations: helm.sh/hook: post-install,post-upgrade diff --git a/charts/redpanda/testdata/template-cases.txtar b/charts/redpanda/testdata/template-cases.txtar index 2a9e1ab80..916c4bdd7 100644 --- a/charts/redpanda/testdata/template-cases.txtar +++ b/charts/redpanda/testdata/template-cases.txtar @@ -1240,3 +1240,32 @@ statefulset: - name: "3rd-party-certs" secret: secretName: "redpanda-external-cert" + +-- gateway-api-tlsroute -- +# Gateway API TLSRoute-based external access with per-listener SNI hostnames. +# Verifies that ClusterIP services and TLSRoute resources are generated, +# and that NodePort/LoadBalancer services are NOT created. +# ASSERT-NO-ERROR +# ASSERT-GOLDEN +external: + enabled: true + gateway: + enabled: true + parentRefs: + - name: kafka-gateway + sectionName: kafka + advertisedPort: 9094 +tls: + enabled: true + certs: + default: + caEnabled: true +listeners: + kafka: + external: + default: + port: 9094 + host: kafka.example.com + hostTemplate: kafka-$POD_ORDINAL-broker.example.com + tls: + cert: default diff --git a/charts/redpanda/tlsroute.go b/charts/redpanda/tlsroute.go index 97995076e..7641bbc29 100644 --- a/charts/redpanda/tlsroute.go +++ b/charts/redpanda/tlsroute.go @@ -15,29 +15,66 @@ import ( "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" ) +// Lightweight TLSRoute types that produce the correct YAML for +// gateway.networking.k8s.io/v1alpha2 TLSRoute resources. +// We define these locally because the upstream Gateway API Go types use type +// aliases (v1alpha2.X = v1.X) that the gotohelm transpiler cannot handle. + +// TLSRoute mirrors the Gateway API TLSRoute resource. +type TLSRoute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec TLSRouteSpec `json:"spec"` +} + +// TLSRouteSpec defines the desired state of a TLSRoute. +type TLSRouteSpec struct { + ParentRefs []TLSRouteParentRef `json:"parentRefs"` + Hostnames []string `json:"hostnames,omitempty"` + Rules []TLSRouteRule `json:"rules"` +} + +// TLSRouteParentRef identifies a parent resource (typically a Gateway). +type TLSRouteParentRef struct { + Group *string `json:"group,omitempty"` + Kind *string `json:"kind,omitempty"` + Name string `json:"name"` + Namespace *string `json:"namespace,omitempty"` + SectionName *string `json:"sectionName,omitempty"` +} + +// TLSRouteRule configures a routing rule. +type TLSRouteRule struct { + BackendRefs []TLSRouteBackendRef `json:"backendRefs,omitempty"` +} + +// TLSRouteBackendRef identifies a backend to route traffic to. +type TLSRouteBackendRef struct { + Name string `json:"name"` + Port int32 `json:"port"` +} + +// DeepCopyObject implements runtime.Object for TLSRoute to satisfy kube.Object. +// +gotohelm:ignore=true +func (t *TLSRoute) DeepCopyObject() runtime.Object { + cp := *t + return &cp +} + // TLSRoutes returns Gateway API TLSRoute resources for external access. -// -// For each enabled external listener (across Kafka, HTTP, Admin, -// SchemaRegistry), this creates: -// - A bootstrap TLSRoute pointing to the bootstrap ClusterIP service -// - Per-broker TLSRoutes pointing to per-broker ClusterIP services -// -// SNI hostnames on each TLSRoute enable the Gateway to route traffic to the -// correct broker based on the client-requested hostname. -func TLSRoutes(state *RenderState) []*gatewayv1alpha2.TLSRoute { +func TLSRoutes(state *RenderState) []*TLSRoute { if !state.Values.External.IsGatewayEnabled() { return nil } gw := state.Values.External.Gateway - parentRefs := toGatewayParentRefs(gw.ParentRefs) + parentRefs := toTLSRouteParentRefs(gw.ParentRefs) labels := FullLabels(state) fullname := Fullname(state) @@ -46,139 +83,71 @@ func TLSRoutes(state *RenderState) []*gatewayv1alpha2.TLSRoute { pods = append(pods, PodNames(state, set)...) } - var routes []*gatewayv1alpha2.TLSRoute + var routes []*TLSRoute - // Kafka listeners for name, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) { if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { continue } - rs := tlsRoutesForListener(tlsRouteParams{ - fullname: fullname, - namespace: state.Release.Namespace, - labels: labels, - parentRefs: parentRefs, - pods: pods, - host: ptr.Deref(listener.Host, ""), - hostTemplate: ptr.Deref(listener.HostTemplate, ""), - name: name, - listenerTag: "kafka", - port: listener.Port, - }) + rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "kafka", listener.Port) routes = append(routes, rs...) } - // HTTP/Pandaproxy listeners for name, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) { if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { continue } - rs := tlsRoutesForListener(tlsRouteParams{ - fullname: fullname, - namespace: state.Release.Namespace, - labels: labels, - parentRefs: parentRefs, - pods: pods, - host: ptr.Deref(listener.Host, ""), - hostTemplate: ptr.Deref(listener.HostTemplate, ""), - name: name, - listenerTag: "http", - port: listener.Port, - }) + rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "http", listener.Port) routes = append(routes, rs...) } - // Admin listeners for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { continue } - rs := tlsRoutesForListener(tlsRouteParams{ - fullname: fullname, - namespace: state.Release.Namespace, - labels: labels, - parentRefs: parentRefs, - pods: pods, - host: ptr.Deref(listener.Host, ""), - hostTemplate: ptr.Deref(listener.HostTemplate, ""), - name: name, - listenerTag: "admin", - port: listener.Port, - }) + rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "admin", listener.Port) routes = append(routes, rs...) } - // Schema Registry listeners for name, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) { if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { continue } - rs := tlsRoutesForListener(tlsRouteParams{ - fullname: fullname, - namespace: state.Release.Namespace, - labels: labels, - parentRefs: parentRefs, - pods: pods, - host: ptr.Deref(listener.Host, ""), - hostTemplate: ptr.Deref(listener.HostTemplate, ""), - name: name, - listenerTag: "schema", - port: listener.Port, - }) + rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "schema", listener.Port) routes = append(routes, rs...) } return routes } -type tlsRouteParams struct { - fullname string - namespace string - labels map[string]string - parentRefs []gatewayv1.ParentReference - pods []string - host string - hostTemplate string - name string - listenerTag string - port int32 -} - -func tlsRoutesForListener(p tlsRouteParams) []*gatewayv1alpha2.TLSRoute { - var routes []*gatewayv1alpha2.TLSRoute +func tlsRoutesForListener(fullname string, namespace string, labels map[string]string, parentRefs []TLSRouteParentRef, pods []string, host string, hostTemplate string, name string, listenerTag string, port int32) []*TLSRoute { + var routes []*TLSRoute - if p.host == "" { + if host == "" { return nil } - bootstrapSvcName := fmt.Sprintf("%s-gateway-bootstrap", p.fullname) + bootstrapSvcName := fmt.Sprintf("%s-gateway-bootstrap", fullname) - // Bootstrap TLSRoute: routes initial client connections to any broker. - bootstrap := &gatewayv1alpha2.TLSRoute{ + bootstrap := &TLSRoute{ TypeMeta: metav1.TypeMeta{ APIVersion: "gateway.networking.k8s.io/v1alpha2", Kind: "TLSRoute", }, ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s-%s-bootstrap", p.fullname, p.listenerTag, p.name), - Namespace: p.namespace, - Labels: p.labels, + Name: fmt.Sprintf("%s-%s-%s-bootstrap", fullname, listenerTag, name), + Namespace: namespace, + Labels: labels, }, - Spec: gatewayv1alpha2.TLSRouteSpec{ - CommonRouteSpec: gatewayv1.CommonRouteSpec{ - ParentRefs: p.parentRefs, - }, - Hostnames: []gatewayv1.Hostname{ - gatewayv1.Hostname(p.host), - }, - Rules: []gatewayv1alpha2.TLSRouteRule{ + Spec: TLSRouteSpec{ + ParentRefs: parentRefs, + Hostnames: []string{host}, + Rules: []TLSRouteRule{ { - BackendRefs: []gatewayv1.BackendRef{ + BackendRefs: []TLSRouteBackendRef{ { - BackendObjectReference: gatewayv1.BackendObjectReference{ - Name: gatewayv1.ObjectName(bootstrapSvcName), - Port: portPtr(p.port), - }, + Name: bootstrapSvcName, + Port: port, }, }, }, @@ -187,41 +156,33 @@ func tlsRoutesForListener(p tlsRouteParams) []*gatewayv1alpha2.TLSRoute { } routes = append(routes, bootstrap) - // Per-broker TLSRoutes: each broker gets a unique SNI hostname so the - // Gateway can route directly to the correct pod. - if p.hostTemplate == "" { + if hostTemplate == "" { return routes } - for i, podname := range p.pods { - brokerHost := renderBrokerHost(p.hostTemplate, i, podname) + for i, podname := range pods { + brokerHost := renderBrokerHost(hostTemplate, i, podname) brokerSvcName := fmt.Sprintf("gw-%s", podname) - route := &gatewayv1alpha2.TLSRoute{ + route := &TLSRoute{ TypeMeta: metav1.TypeMeta{ APIVersion: "gateway.networking.k8s.io/v1alpha2", Kind: "TLSRoute", }, ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s-%s-%d", p.fullname, p.listenerTag, p.name, i), - Namespace: p.namespace, - Labels: p.labels, + Name: fmt.Sprintf("%s-%s-%s-%d", fullname, listenerTag, name, i), + Namespace: namespace, + Labels: labels, }, - Spec: gatewayv1alpha2.TLSRouteSpec{ - CommonRouteSpec: gatewayv1.CommonRouteSpec{ - ParentRefs: p.parentRefs, - }, - Hostnames: []gatewayv1.Hostname{ - gatewayv1.Hostname(brokerHost), - }, - Rules: []gatewayv1alpha2.TLSRouteRule{ + Spec: TLSRouteSpec{ + ParentRefs: parentRefs, + Hostnames: []string{brokerHost}, + Rules: []TLSRouteRule{ { - BackendRefs: []gatewayv1.BackendRef{ + BackendRefs: []TLSRouteBackendRef{ { - BackendObjectReference: gatewayv1.BackendObjectReference{ - Name: gatewayv1.ObjectName(brokerSvcName), - Port: portPtr(p.port), - }, + Name: brokerSvcName, + Port: port, }, }, }, @@ -234,44 +195,23 @@ func tlsRoutesForListener(p tlsRouteParams) []*gatewayv1alpha2.TLSRoute { return routes } -// toGatewayParentRefs converts the chart's GatewayParentRef values into the -// Gateway API ParentReference type. -func toGatewayParentRefs(refs []GatewayParentRef) []gatewayv1.ParentReference { - var parentRefs []gatewayv1.ParentReference +func toTLSRouteParentRefs(refs []GatewayParentRef) []TLSRouteParentRef { + var parentRefs []TLSRouteParentRef for _, ref := range refs { - pr := gatewayv1.ParentReference{ - Name: gatewayv1.ObjectName(ref.Name), - } - if ref.Group != nil { - g := gatewayv1.Group(*ref.Group) - pr.Group = &g - } - if ref.Kind != nil { - k := gatewayv1.Kind(*ref.Kind) - pr.Kind = &k - } - if ref.Namespace != nil { - ns := gatewayv1.Namespace(*ref.Namespace) - pr.Namespace = &ns - } - if ref.SectionName != nil { - sn := gatewayv1.SectionName(*ref.SectionName) - pr.SectionName = &sn + pr := TLSRouteParentRef{ + Name: ref.Name, + Group: ref.Group, + Kind: ref.Kind, + Namespace: ref.Namespace, + SectionName: ref.SectionName, } parentRefs = append(parentRefs, pr) } return parentRefs } -// renderBrokerHost interpolates the host template with broker-specific values. -// Supports $POD_ORDINAL and $POD_NAME. func renderBrokerHost(tmpl string, ordinal int, podName string) string { result := strings.ReplaceAll(tmpl, "$POD_ORDINAL", fmt.Sprintf("%d", ordinal)) result = strings.ReplaceAll(result, "$POD_NAME", podName) return result } - -func portPtr(port int32) *gatewayv1.PortNumber { - p := gatewayv1.PortNumber(port) - return &p -} diff --git a/charts/redpanda/values_partial.gen.go b/charts/redpanda/values_partial.gen.go index 626ad73c2..1dfba1ee2 100644 --- a/charts/redpanda/values_partial.gen.go +++ b/charts/redpanda/values_partial.gen.go @@ -95,15 +95,16 @@ type PartialTLS struct { } type PartialExternalConfig struct { - Addresses []string "json:\"addresses,omitempty\"" - Annotations map[string]string "json:\"annotations,omitempty\"" - Domain *string "json:\"domain,omitempty\"" - Enabled *bool "json:\"enabled,omitempty\" jsonschema:\"required\"" - Type *corev1.ServiceType "json:\"type,omitempty\" jsonschema:\"pattern=^(LoadBalancer|NodePort)$\"" - PrefixTemplate *string "json:\"prefixTemplate,omitempty\"" - SourceRanges []string "json:\"sourceRanges,omitempty\"" - Service *PartialEnableable "json:\"service,omitempty\"" - ExternalDNS *PartialEnableable "json:\"externalDns,omitempty\"" + Addresses []string "json:\"addresses,omitempty\"" + Annotations map[string]string "json:\"annotations,omitempty\"" + Domain *string "json:\"domain,omitempty\"" + Enabled *bool "json:\"enabled,omitempty\" jsonschema:\"required\"" + Type *corev1.ServiceType "json:\"type,omitempty\" jsonschema:\"pattern=^(LoadBalancer|NodePort)$\"" + PrefixTemplate *string "json:\"prefixTemplate,omitempty\"" + SourceRanges []string "json:\"sourceRanges,omitempty\"" + Service *PartialEnableable "json:\"service,omitempty\"" + ExternalDNS *PartialEnableable "json:\"externalDns,omitempty\"" + Gateway *PartialGatewayConfig "json:\"gateway,omitempty\"" } type PartialLogging struct { @@ -325,6 +326,12 @@ type PartialSASLAuth struct { BootstrapUser *PartialBootstrapUser "json:\"bootstrapUser,omitempty\"" } +type PartialGatewayConfig struct { + Enabled *bool "json:\"enabled,omitempty\"" + ParentRefs []PartialGatewayParentRef "json:\"parentRefs,omitempty\"" + AdvertisedPort *int32 "json:\"advertisedPort,omitempty\"" +} + type PartialListenerConfig[T ~string] struct { Enabled *bool "json:\"enabled,omitempty\"" External map[string]PartialExternalListener[T] "json:\"external,omitempty\"" @@ -403,6 +410,14 @@ type PartialSASLUser struct { Mechanism *SASLMechanism "json:\"mechanism,omitempty\"" } +type PartialGatewayParentRef struct { + Group *string "json:\"group,omitempty\"" + Kind *string "json:\"kind,omitempty\"" + Name *string "json:\"name,omitempty\"" + Namespace *string "json:\"namespace,omitempty\"" + SectionName *string "json:\"sectionName,omitempty\"" +} + type PartialExternalListener[T ~string] struct { Enabled *bool "json:\"enabled,omitempty\"" AdvertisedPorts []int32 "json:\"advertisedPorts,omitempty\" jsonschema:\"minItems=1\"" @@ -411,6 +426,8 @@ type PartialExternalListener[T ~string] struct { TLS *PartialExternalTLS "json:\"tls,omitempty\"" AuthenticationMethod *T "json:\"authenticationMethod,omitempty\"" PrefixTemplate *string "json:\"prefixTemplate,omitempty\"" + Host *string "json:\"host,omitempty\"" + HostTemplate *string "json:\"hostTemplate,omitempty\"" } type PartialTrustStore struct { diff --git a/operator/api/applyconfiguration/redpanda/v1alpha2/external.go b/operator/api/applyconfiguration/redpanda/v1alpha2/external.go index b2eb0df38..a4bca90ba 100644 --- a/operator/api/applyconfiguration/redpanda/v1alpha2/external.go +++ b/operator/api/applyconfiguration/redpanda/v1alpha2/external.go @@ -14,15 +14,16 @@ package v1alpha2 // ExternalApplyConfiguration represents a declarative configuration of the External type for use // with apply. type ExternalApplyConfiguration struct { - Addresses []string `json:"addresses,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - Domain *string `json:"domain,omitempty"` - Enabled *bool `json:"enabled,omitempty"` - Service *ExternalServiceApplyConfiguration `json:"service,omitempty"` - SourceRanges []string `json:"sourceRanges,omitempty"` - Type *string `json:"type,omitempty"` - ExternalDNS *ExternalDNSApplyConfiguration `json:"externalDns,omitempty"` - PrefixTemplate *string `json:"prefixTemplate,omitempty"` + Addresses []string `json:"addresses,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + Domain *string `json:"domain,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Service *ExternalServiceApplyConfiguration `json:"service,omitempty"` + SourceRanges []string `json:"sourceRanges,omitempty"` + Type *string `json:"type,omitempty"` + ExternalDNS *ExternalDNSApplyConfiguration `json:"externalDns,omitempty"` + PrefixTemplate *string `json:"prefixTemplate,omitempty"` + Gateway *GatewayExternalConfigApplyConfiguration `json:"gateway,omitempty"` } // ExternalApplyConfiguration constructs a declarative configuration of the External type for use with @@ -112,3 +113,11 @@ func (b *ExternalApplyConfiguration) WithPrefixTemplate(value string) *ExternalA b.PrefixTemplate = &value return b } + +// WithGateway sets the Gateway field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Gateway field is set to the value of the last call. +func (b *ExternalApplyConfiguration) WithGateway(value *GatewayExternalConfigApplyConfiguration) *ExternalApplyConfiguration { + b.Gateway = value + return b +} diff --git a/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayexternalconfig.go b/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayexternalconfig.go new file mode 100644 index 000000000..ec59050a1 --- /dev/null +++ b/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayexternalconfig.go @@ -0,0 +1,55 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha2 + +// GatewayExternalConfigApplyConfiguration represents a declarative configuration of the GatewayExternalConfig type for use +// with apply. +type GatewayExternalConfigApplyConfiguration struct { + Enabled *bool `json:"enabled,omitempty"` + ParentRefs []GatewayParentRefConfigApplyConfiguration `json:"parentRefs,omitempty"` + AdvertisedPort *int32 `json:"advertisedPort,omitempty"` +} + +// GatewayExternalConfigApplyConfiguration constructs a declarative configuration of the GatewayExternalConfig type for use with +// apply. +func GatewayExternalConfig() *GatewayExternalConfigApplyConfiguration { + return &GatewayExternalConfigApplyConfiguration{} +} + +// WithEnabled sets the Enabled field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Enabled field is set to the value of the last call. +func (b *GatewayExternalConfigApplyConfiguration) WithEnabled(value bool) *GatewayExternalConfigApplyConfiguration { + b.Enabled = &value + return b +} + +// WithParentRefs adds the given value to the ParentRefs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ParentRefs field. +func (b *GatewayExternalConfigApplyConfiguration) WithParentRefs(values ...*GatewayParentRefConfigApplyConfiguration) *GatewayExternalConfigApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithParentRefs") + } + b.ParentRefs = append(b.ParentRefs, *values[i]) + } + return b +} + +// WithAdvertisedPort sets the AdvertisedPort field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdvertisedPort field is set to the value of the last call. +func (b *GatewayExternalConfigApplyConfiguration) WithAdvertisedPort(value int32) *GatewayExternalConfigApplyConfiguration { + b.AdvertisedPort = &value + return b +} diff --git a/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayparentrefconfig.go b/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayparentrefconfig.go new file mode 100644 index 000000000..a52231a60 --- /dev/null +++ b/operator/api/applyconfiguration/redpanda/v1alpha2/gatewayparentrefconfig.go @@ -0,0 +1,68 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha2 + +// GatewayParentRefConfigApplyConfiguration represents a declarative configuration of the GatewayParentRefConfig type for use +// with apply. +type GatewayParentRefConfigApplyConfiguration struct { + Group *string `json:"group,omitempty"` + Kind *string `json:"kind,omitempty"` + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` + SectionName *string `json:"sectionName,omitempty"` +} + +// GatewayParentRefConfigApplyConfiguration constructs a declarative configuration of the GatewayParentRefConfig type for use with +// apply. +func GatewayParentRefConfig() *GatewayParentRefConfigApplyConfiguration { + return &GatewayParentRefConfigApplyConfiguration{} +} + +// WithGroup sets the Group field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Group field is set to the value of the last call. +func (b *GatewayParentRefConfigApplyConfiguration) WithGroup(value string) *GatewayParentRefConfigApplyConfiguration { + b.Group = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *GatewayParentRefConfigApplyConfiguration) WithKind(value string) *GatewayParentRefConfigApplyConfiguration { + b.Kind = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *GatewayParentRefConfigApplyConfiguration) WithName(value string) *GatewayParentRefConfigApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *GatewayParentRefConfigApplyConfiguration) WithNamespace(value string) *GatewayParentRefConfigApplyConfiguration { + b.Namespace = &value + return b +} + +// WithSectionName sets the SectionName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SectionName field is set to the value of the last call. +func (b *GatewayParentRefConfigApplyConfiguration) WithSectionName(value string) *GatewayParentRefConfigApplyConfiguration { + b.SectionName = &value + return b +} diff --git a/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc b/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc index ba6a379ae..e199a5158 100644 --- a/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc +++ b/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc @@ -1190,6 +1190,7 @@ External defines external connectivity settings in the Helm values. | *`type`* __string__ | Specifies the external Service type. Only NodePort and LoadBalancer are supported. If undefined, then advertised listeners will be configured in Redpanda, but the Helm chart will not create a Service. NodePort is recommended in cases where latency is a priority. + | | | *`externalDns`* __xref:{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-externaldns[$$ExternalDNS$$]__ | Defines externalDNS configurations. + | | | *`prefixTemplate`* __string__ | Specifies a naming prefix template for external Services. + | | +| *`gateway`* __xref:{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-gatewayexternalconfig[$$GatewayExternalConfig$$]__ | Configures Gateway API TLSRoute-based external access. When enabled, ClusterIP services and TLSRoute resources are created instead of NodePort/LoadBalancer services. The Gateway itself must be managed externally. + | | |=== @@ -1248,6 +1249,8 @@ internal and external listeners. However, it is ignored when specified + on internal listeners. + | | | *`advertisedPorts`* __integer array__ | Specifies the network port that the external Service listens on. + | | | *`nodePort`* __integer__ | | | +| *`host`* __string__ | Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. + | | +| *`hostTemplate`* __string__ | HostTemplate is a Go template for per-broker TLSRoute SNI hostnames. Supports $POD_ORDINAL and $POD_NAME variables. + | | |=== @@ -1334,6 +1337,54 @@ FilterType specifies the type, either include or exclude of a consumer group fil |=== +[id="{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-gatewayexternalconfig"] +==== GatewayExternalConfig + + + +GatewayExternalConfig holds configuration for Gateway API-based external access using TLSRoute resources with SNI-based routing. + + + +.Appears In: +**** +- xref:{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-external[$$External$$] +**** + +[cols="20a,50a,15a,15a", options="header"] +|=== +| Field | Description | Default | Validation +| *`enabled`* __boolean__ | Enables Gateway API TLSRoute-based external access. + | | +| *`parentRefs`* __xref:{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-gatewayparentrefconfig[$$GatewayParentRefConfig$$] array__ | Defines which Gateway(s) handle the TLSRoutes. At least one parent reference must be provided. + | | +| *`advertisedPort`* __integer__ | The port advertised to clients. Defaults to 443. + | | +|=== + + +[id="{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-gatewayparentrefconfig"] +==== GatewayParentRefConfig + + + +GatewayParentRefConfig identifies a Gateway (or ListenerSet) that should handle the TLSRoute traffic. Schema mirrors the upstream Gateway API ParentReference. + + + +.Appears In: +**** +- xref:{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-gatewayexternalconfig[$$GatewayExternalConfig$$] +**** + +[cols="20a,50a,15a,15a", options="header"] +|=== +| Field | Description | Default | Validation +| *`group`* __string__ | API group of the referent. Defaults to "gateway.networking.k8s.io". + | | +| *`kind`* __string__ | Kind of the referent. Defaults to "Gateway". + | | +| *`name`* __string__ | Name of the referent. + | | +| *`namespace`* __string__ | Namespace of the referent. + | | +| *`sectionName`* __string__ | Name of a section within the target resource. + | | +|=== + + [id="{anchor_prefix}-github-com-redpanda-data-redpanda-operator-operator-api-redpanda-v1alpha2-group"] ==== Group diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go b/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go index b025e449d..7dd5a97d2 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go @@ -1524,6 +1524,11 @@ func (in *External) DeepCopyInto(out *External) { *out = new(string) **out = **in } + if in.Gateway != nil { + in, out := &in.Gateway, &out.Gateway + *out = new(GatewayExternalConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new External. @@ -1570,6 +1575,16 @@ func (in *ExternalListener) DeepCopyInto(out *ExternalListener) { *out = new(int32) **out = **in } + if in.Host != nil { + in, out := &in.Host, &out.Host + *out = new(string) + **out = **in + } + if in.HostTemplate != nil { + in, out := &in.HostTemplate, &out.HostTemplate + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalListener. @@ -1652,6 +1667,73 @@ func (in *FsValidator) DeepCopy() *FsValidator { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayExternalConfig) DeepCopyInto(out *GatewayExternalConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.ParentRefs != nil { + in, out := &in.ParentRefs, &out.ParentRefs + *out = make([]GatewayParentRefConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdvertisedPort != nil { + in, out := &in.AdvertisedPort, &out.AdvertisedPort + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayExternalConfig. +func (in *GatewayExternalConfig) DeepCopy() *GatewayExternalConfig { + if in == nil { + return nil + } + out := new(GatewayExternalConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayParentRefConfig) DeepCopyInto(out *GatewayParentRefConfig) { + *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(string) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(string) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } + if in.SectionName != nil { + in, out := &in.SectionName, &out.SectionName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayParentRefConfig. +func (in *GatewayParentRefConfig) DeepCopy() *GatewayParentRefConfig { + if in == nil { + return nil + } + out := new(GatewayParentRefConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Group) DeepCopyInto(out *Group) { *out = *in diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go index 63f738191..06982f9c4 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go @@ -30,11 +30,12 @@ package v1alpha2 import ( "testing" - "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" ) func TestDeprecatedFieldWarnings(t *testing.T) { diff --git a/operator/chart/files/rbac/v2-manager.ClusterRole.yaml b/operator/chart/files/rbac/v2-manager.ClusterRole.yaml index 6c331a9d2..e1e6d0a97 100644 --- a/operator/chart/files/rbac/v2-manager.ClusterRole.yaml +++ b/operator/chart/files/rbac/v2-manager.ClusterRole.yaml @@ -178,6 +178,18 @@ rules: - patch - update - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: diff --git a/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml b/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml index 21609c088..0cef52790 100644 --- a/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml +++ b/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml @@ -1867,6 +1867,51 @@ spec: matches `external.addresses[i]`.`external.domain`. type: boolean type: object + gateway: + description: Configures Gateway API TLSRoute-based external + access. When enabled, ClusterIP services and TLSRoute resources + are created instead of NodePort/LoadBalancer services. The + Gateway itself must be managed externally. + properties: + advertisedPort: + description: The port advertised to clients. Defaults + to 443. + format: int32 + type: integer + enabled: + description: Enables Gateway API TLSRoute-based external + access. + type: boolean + parentRefs: + description: Defines which Gateway(s) handle the TLSRoutes. + At least one parent reference must be provided. + items: + description: GatewayParentRefConfig identifies a Gateway + (or ListenerSet) that should handle the TLSRoute traffic. + Schema mirrors the upstream Gateway API ParentReference. + properties: + group: + description: API group of the referent. Defaults + to "gateway.networking.k8s.io". + type: string + kind: + description: Kind of the referent. Defaults to "Gateway". + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent. + type: string + sectionName: + description: Name of a section within the target + resource. + type: string + required: + - name + type: object + type: array + type: object prefixTemplate: description: Specifies a naming prefix template for external Services. @@ -1996,6 +2041,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -2229,6 +2283,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -2467,6 +2530,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -2788,6 +2860,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -35661,6 +35742,51 @@ spec: matches `external.addresses[i]`.`external.domain`. type: boolean type: object + gateway: + description: Configures Gateway API TLSRoute-based external + access. When enabled, ClusterIP services and TLSRoute resources + are created instead of NodePort/LoadBalancer services. The + Gateway itself must be managed externally. + properties: + advertisedPort: + description: The port advertised to clients. Defaults + to 443. + format: int32 + type: integer + enabled: + description: Enables Gateway API TLSRoute-based external + access. + type: boolean + parentRefs: + description: Defines which Gateway(s) handle the TLSRoutes. + At least one parent reference must be provided. + items: + description: GatewayParentRefConfig identifies a Gateway + (or ListenerSet) that should handle the TLSRoute traffic. + Schema mirrors the upstream Gateway API ParentReference. + properties: + group: + description: API group of the referent. Defaults + to "gateway.networking.k8s.io". + type: string + kind: + description: Kind of the referent. Defaults to "Gateway". + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent. + type: string + sectionName: + description: Name of a section within the target + resource. + type: string + required: + - name + type: object + type: array + type: object prefixTemplate: description: Specifies a naming prefix template for external Services. @@ -35790,6 +35916,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -36023,6 +36158,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -36261,6 +36405,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer @@ -36582,6 +36735,15 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + host: + description: Host is the SNI hostname for the bootstrap + TLSRoute when using Gateway API external access. + type: string + hostTemplate: + description: HostTemplate is a Go template for per-broker + TLSRoute SNI hostnames. Supports $POD_ORDINAL + and $POD_NAME variables. + type: string nodePort: format: int32 type: integer diff --git a/operator/config/crd/bases/cluster.redpanda.com_stretchclusters.yaml b/operator/config/crd/bases/cluster.redpanda.com_stretchclusters.yaml index 4e1fc0a44..1440dd017 100644 --- a/operator/config/crd/bases/cluster.redpanda.com_stretchclusters.yaml +++ b/operator/config/crd/bases/cluster.redpanda.com_stretchclusters.yaml @@ -383,6 +383,48 @@ spec: `external.addresses[i]`.`external.domain`. type: boolean type: object + gateway: + description: Configures Gateway API TLSRoute-based external access. + When enabled, ClusterIP services and TLSRoute resources are + created instead of NodePort/LoadBalancer services. The Gateway + itself must be managed externally. + properties: + advertisedPort: + description: The port advertised to clients. Defaults to 443. + format: int32 + type: integer + enabled: + description: Enables Gateway API TLSRoute-based external access. + type: boolean + parentRefs: + description: Defines which Gateway(s) handle the TLSRoutes. + At least one parent reference must be provided. + items: + description: GatewayParentRefConfig identifies a Gateway + (or ListenerSet) that should handle the TLSRoute traffic. + Schema mirrors the upstream Gateway API ParentReference. + properties: + group: + description: API group of the referent. Defaults to + "gateway.networking.k8s.io". + type: string + kind: + description: Kind of the referent. Defaults to "Gateway". + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent. + type: string + sectionName: + description: Name of a section within the target resource. + type: string + required: + - name + type: object + type: array + type: object prefixTemplate: description: Specifies a naming prefix template for external Services. type: string diff --git a/operator/config/rbac/bases/operator/role.yaml b/operator/config/rbac/bases/operator/role.yaml index da5c8445b..c0105f2d2 100644 --- a/operator/config/rbac/bases/operator/role.yaml +++ b/operator/config/rbac/bases/operator/role.yaml @@ -229,6 +229,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: diff --git a/operator/config/rbac/itemized/v2-manager.yaml b/operator/config/rbac/itemized/v2-manager.yaml index 0b832109b..e4de3cdce 100644 --- a/operator/config/rbac/itemized/v2-manager.yaml +++ b/operator/config/rbac/itemized/v2-manager.yaml @@ -178,6 +178,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: diff --git a/operator/internal/controller/redpanda/testdata/role.yaml b/operator/internal/controller/redpanda/testdata/role.yaml index d2087cd0b..a4b8c71b6 100644 --- a/operator/internal/controller/redpanda/testdata/role.yaml +++ b/operator/internal/controller/redpanda/testdata/role.yaml @@ -210,6 +210,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: diff --git a/operator/internal/statuses/zz_generated_status.go b/operator/internal/statuses/zz_generated_status.go index fad25e6f6..9b579157a 100644 --- a/operator/internal/statuses/zz_generated_status.go +++ b/operator/internal/statuses/zz_generated_status.go @@ -6,12 +6,12 @@ import ( "strings" "time" - "github.com/redpanda-data/common-go/rp-controller-utils/status" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/redpanda-data/common-go/rp-controller-utils/status" redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2" ) diff --git a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go index 78cfcbe54..91a3cc0f6 100644 --- a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go +++ b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go @@ -1,3 +1,12 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 diff --git a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go index 33fbe01d2..66544b428 100644 --- a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go +++ b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go @@ -1,3 +1,12 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 From 6addc45713be760e6f59be091ebd1c5145cc7bd2 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 19:59:36 -0700 Subject: [PATCH 03/13] charts/redpanda: Per-listener gateway opt-in for gradual migration Changes gateway mode from a global all-or-nothing switch to a per-listener opt-in. Each external listener can independently set `gateway: true` to use TLSRoute mode while other listeners remain on NodePort/LoadBalancer. This enables gradual migration: 1. Deploy Gateway, configure external.gateway with parentRefs 2. Add a new listener with gateway: true alongside existing ones 3. Migrate clients to the new TLSRoute-based listener 4. Remove the old NodePort/LoadBalancer listener Key changes: - Add Gateway *bool field to ExternalListener - ServicePorts() skips gateway-enabled listeners (NodePort/LB) - gatewayServicePorts() only includes gateway-enabled listeners - TLSRoutes only created for listeners with gateway: true - advertisedHostJSON checks per-listener gateway flag - Remove global NodePort/LB suppression guards - Add gateway-api-migration test case showing dual-mode operation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../redpanda/chart/templates/_secrets.go.tpl | 103 +- .../chart/templates/_service.gateway.go.tpl | 8 +- .../templates/_service.loadbalancer.go.tpl | 5 - .../chart/templates/_service.nodeport.go.tpl | 23 +- .../redpanda/chart/templates/_tlsroute.go.tpl | 8 +- .../redpanda/chart/templates/_values.go.tpl | 63 +- charts/redpanda/chart/values.schema.json | 12 + charts/redpanda/secrets.go | 21 +- charts/redpanda/service.gateway.go | 8 +- charts/redpanda/service.loadbalancer.go | 5 - charts/redpanda/service.nodeport.go | 25 +- .../testdata/template-cases.golden.txtar | 1757 ++++++++++++++++- charts/redpanda/testdata/template-cases.txtar | 38 + charts/redpanda/tlsroute.go | 8 +- charts/redpanda/values.go | 27 +- charts/redpanda/values_partial.gen.go | 1 + .../v1alpha2/redpanda_clusterspec_types.go | 2 + .../redpanda/v1alpha2/testdata/crd-docs.adoc | 1 + .../v1alpha2/zz_generated.deepcopy.go | 5 + .../bases/cluster.redpanda.com_redpandas.yaml | 56 + 20 files changed, 2031 insertions(+), 145 deletions(-) diff --git a/charts/redpanda/chart/templates/_secrets.go.tpl b/charts/redpanda/chart/templates/_secrets.go.tpl index cf30fc93b..a0d648ed6 100644 --- a/charts/redpanda/chart/templates/_secrets.go.tpl +++ b/charts/redpanda/chart/templates/_secrets.go.tpl @@ -346,7 +346,7 @@ echo "passed"`) -}} {{- $replicaIndex := (index .a 3) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} +{{- if (and (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") (get (fromJson (include "redpanda.isListenerGatewayEnabled" (dict "a" (list $state $externalName)))) "r")) -}} {{- $_is_returning = true -}} {{- (dict "r" (get (fromJson (include "redpanda.advertisedHostJSONGateway" (dict "a" (list $state $externalName $replicaIndex)))) "r")) | toJson -}} {{- break -}} @@ -407,33 +407,33 @@ echo "passed"`) -}} {{- $name := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_618_l_6_ok_7 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_6 := (index $_618_l_6_ok_7 0) -}} -{{- $ok_7 := (index $_618_l_6_ok_7 1) -}} +{{- $_619_l_6_ok_7 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_6 := (index $_619_l_6_ok_7 0) -}} +{{- $ok_7 := (index $_619_l_6_ok_7 1) -}} {{- if $ok_7 -}} {{- $_is_returning = true -}} {{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_6.hostTemplate "")))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_622_l_8_ok_9 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_8 := (index $_622_l_8_ok_9 0) -}} -{{- $ok_9 := (index $_622_l_8_ok_9 1) -}} +{{- $_623_l_8_ok_9 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_8 := (index $_623_l_8_ok_9 0) -}} +{{- $ok_9 := (index $_623_l_8_ok_9 1) -}} {{- if $ok_9 -}} {{- $_is_returning = true -}} {{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_8.hostTemplate "")))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_626_l_10_ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_10 := (index $_626_l_10_ok_11 0) -}} -{{- $ok_11 := (index $_626_l_10_ok_11 1) -}} +{{- $_627_l_10_ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_10 := (index $_627_l_10_ok_11 0) -}} +{{- $ok_11 := (index $_627_l_10_ok_11 1) -}} {{- if $ok_11 -}} {{- $_is_returning = true -}} {{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_10.hostTemplate "")))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_630_l_12_ok_13 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_12 := (index $_630_l_12_ok_13 0) -}} -{{- $ok_13 := (index $_630_l_12_ok_13 1) -}} +{{- $_631_l_12_ok_13 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_12 := (index $_631_l_12_ok_13 0) -}} +{{- $ok_13 := (index $_631_l_12_ok_13 1) -}} {{- if $ok_13 -}} {{- $_is_returning = true -}} {{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_12.hostTemplate "")))) "r")) | toJson -}} @@ -445,41 +445,84 @@ echo "passed"`) -}} {{- end -}} {{- end -}} -{{- define "redpanda.findListenerHost" -}} +{{- define "redpanda.isListenerGatewayEnabled" -}} {{- $state := (index .a 0) -}} {{- $name := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_640_l_14_ok_15 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_14 := (index $_640_l_14_ok_15 0) -}} -{{- $ok_15 := (index $_640_l_14_ok_15 1) -}} +{{- $_641_l_14_ok_15 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_14 := (index $_641_l_14_ok_15 0) -}} +{{- $ok_15 := (index $_641_l_14_ok_15 1) -}} {{- if $ok_15 -}} {{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_14.host "")))) "r")) | toJson -}} +{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_14)))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_644_l_16_ok_17 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_16 := (index $_644_l_16_ok_17 0) -}} -{{- $ok_17 := (index $_644_l_16_ok_17 1) -}} +{{- $_645_l_16_ok_17 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_16 := (index $_645_l_16_ok_17 0) -}} +{{- $ok_17 := (index $_645_l_16_ok_17 1) -}} {{- if $ok_17 -}} {{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_16.host "")))) "r")) | toJson -}} +{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_16)))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_648_l_18_ok_19 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_18 := (index $_648_l_18_ok_19 0) -}} -{{- $ok_19 := (index $_648_l_18_ok_19 1) -}} +{{- $_649_l_18_ok_19 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_18 := (index $_649_l_18_ok_19 0) -}} +{{- $ok_19 := (index $_649_l_18_ok_19 1) -}} {{- if $ok_19 -}} {{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_18.host "")))) "r")) | toJson -}} +{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_18)))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $_652_l_20_ok_21 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_20 := (index $_652_l_20_ok_21 0) -}} -{{- $ok_21 := (index $_652_l_20_ok_21 1) -}} +{{- $_653_l_20_ok_21 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_20 := (index $_653_l_20_ok_21 0) -}} +{{- $ok_21 := (index $_653_l_20_ok_21 1) -}} {{- if $ok_21 -}} {{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_20.host "")))) "r")) | toJson -}} +{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_20)))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" false) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "redpanda.findListenerHost" -}} +{{- $state := (index .a 0) -}} +{{- $name := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_663_l_22_ok_23 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_22 := (index $_663_l_22_ok_23 0) -}} +{{- $ok_23 := (index $_663_l_22_ok_23 1) -}} +{{- if $ok_23 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_22.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_667_l_24_ok_25 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_24 := (index $_667_l_24_ok_25 0) -}} +{{- $ok_25 := (index $_667_l_24_ok_25 1) -}} +{{- if $ok_25 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_24.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_671_l_26_ok_27 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_26 := (index $_671_l_26_ok_27 0) -}} +{{- $ok_27 := (index $_671_l_26_ok_27 1) -}} +{{- if $ok_27 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_26.host "")))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_675_l_28_ok_29 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} +{{- $l_28 := (index $_675_l_28_ok_29 0) -}} +{{- $ok_29 := (index $_675_l_28_ok_29 1) -}} +{{- if $ok_29 -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_28.host "")))) "r")) | toJson -}} {{- break -}} {{- end -}} {{- $_is_returning = true -}} diff --git a/charts/redpanda/chart/templates/_service.gateway.go.tpl b/charts/redpanda/chart/templates/_service.gateway.go.tpl index 61c157efc..7690aa070 100644 --- a/charts/redpanda/chart/templates/_service.gateway.go.tpl +++ b/charts/redpanda/chart/templates/_service.gateway.go.tpl @@ -55,7 +55,7 @@ {{- $_is_returning := false -}} {{- $ports := (coalesce nil) -}} {{- range $name, $listener := $state.Values.listeners.admin.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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))))) -}} @@ -64,7 +64,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.kafka.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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))))) -}} @@ -73,7 +73,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.http.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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))))) -}} @@ -82,7 +82,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.schemaRegistry.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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))))) -}} diff --git a/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl b/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl index edc0ff8d2..4ed4e8a59 100644 --- a/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl +++ b/charts/redpanda/chart/templates/_service.loadbalancer.go.tpl @@ -10,11 +10,6 @@ {{- (dict "r" (coalesce nil)) | toJson -}} {{- break -}} {{- end -}} -{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} -{{- $_is_returning = true -}} -{{- (dict "r" (coalesce nil)) | toJson -}} -{{- break -}} -{{- end -}} {{- if (ne $state.Values.external.type "LoadBalancer") -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} diff --git a/charts/redpanda/chart/templates/_service.nodeport.go.tpl b/charts/redpanda/chart/templates/_service.nodeport.go.tpl index 1c9747c20..33e055614 100644 --- a/charts/redpanda/chart/templates/_service.nodeport.go.tpl +++ b/charts/redpanda/chart/templates/_service.nodeport.go.tpl @@ -10,21 +10,20 @@ {{- (dict "r" (coalesce nil)) | toJson -}} {{- break -}} {{- end -}} -{{- if (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") -}} -{{- $_is_returning = true -}} -{{- (dict "r" (coalesce nil)) | toJson -}} -{{- break -}} -{{- end -}} {{- if (ne $state.Values.external.type "NodePort") -}} {{- $_is_returning = true -}} {{- (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)) -}} @@ -38,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)) -}} @@ -51,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)) -}} @@ -64,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)) -}} @@ -73,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) -}} diff --git a/charts/redpanda/chart/templates/_tlsroute.go.tpl b/charts/redpanda/chart/templates/_tlsroute.go.tpl index bdf3a7753..c52bc8c5a 100644 --- a/charts/redpanda/chart/templates/_tlsroute.go.tpl +++ b/charts/redpanda/chart/templates/_tlsroute.go.tpl @@ -23,7 +23,7 @@ {{- end -}} {{- $routes := (coalesce nil) -}} {{- range $name, $listener := $state.Values.listeners.kafka.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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 -}} {{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "kafka" ($listener.port | int))))) "r") -}} @@ -33,7 +33,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.http.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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 -}} {{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "http" ($listener.port | int))))) "r") -}} @@ -43,7 +43,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.admin.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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 -}} {{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "admin" ($listener.port | int))))) "r") -}} @@ -53,7 +53,7 @@ {{- break -}} {{- end -}} {{- range $name, $listener := $state.Values.listeners.schemaRegistry.external -}} -{{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $state.Values.external.enabled)))) "r")) -}} +{{- 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 -}} {{- $rs := (get (fromJson (include "redpanda.tlsRoutesForListener" (dict "a" (list $fullname $state.Release.Namespace $labels $parentRefs $pods (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.host "")))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.hostTemplate "")))) "r") $name "schema" ($listener.port | int))))) "r") -}} diff --git a/charts/redpanda/chart/templates/_values.go.tpl b/charts/redpanda/chart/templates/_values.go.tpl index 395cf5a69..1d3de80ff 100644 --- a/charts/redpanda/chart/templates/_values.go.tpl +++ b/charts/redpanda/chart/templates/_values.go.tpl @@ -1205,6 +1205,9 @@ {{- if (not (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.enabled $external.enabled)))) "r")) -}} {{- continue -}} {{- end -}} +{{- if (and (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $external)))) "r") (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.gateway false)))) "r")) -}} +{{- continue -}} +{{- end -}} {{- $fallbackPorts := (concat (default (list) $listener.advertisedPorts) (list ($l.port | int))) -}} {{- $ports = (concat (default (list) $ports) (list (mustMergeOverwrite (dict "port" 0 "targetPort" 0) (dict "name" (printf "%s-%s" $namePrefix $name) "protocol" "TCP" "appProtocol" $l.appProtocol "targetPort" ($listener.port | int) "port" ((get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $listener.nodePort (index $fallbackPorts (0 | int)))))) "r") | int))))) -}} {{- end -}} @@ -1311,7 +1314,7 @@ {{- $auth = $authAStr -}} {{- end -}} {{- $_is_returning = true -}} -{{- (dict "r" (mustMergeOverwrite (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)) (dict "enabled" $l.enabled "advertisedPorts" $l.advertisedPorts "port" ($l.port | int) "nodePort" $l.nodePort "tls" $l.tls "authenticationMethod" $auth "prefixTemplate" $l.prefixTemplate "host" $l.host "hostTemplate" $l.hostTemplate))) | toJson -}} +{{- (dict "r" (mustMergeOverwrite (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)) (dict "enabled" $l.enabled "advertisedPorts" $l.advertisedPorts "port" ($l.port | int) "nodePort" $l.nodePort "tls" $l.tls "authenticationMethod" $auth "prefixTemplate" $l.prefixTemplate "gateway" $l.gateway "host" $l.host "hostTemplate" $l.hostTemplate))) | toJson -}} {{- break -}} {{- end -}} {{- end -}} @@ -1326,6 +1329,16 @@ {{- end -}} {{- end -}} +{{- define "redpanda.ExternalListener.IsGatewayListener" -}} +{{- $l := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.gateway false)))) "r")) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + {{- define "redpanda.TunableConfig.Translate" -}} {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} @@ -1357,9 +1370,9 @@ {{- $result := (dict) -}} {{- range $k, $v := $c -}} {{- if (not (empty $v)) -}} -{{- $_1911___ok_15 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v)))) "r") -}} -{{- $_ := ((index $_1911___ok_15 0) | float64) -}} -{{- $ok_15 := (index $_1911___ok_15 1) -}} +{{- $_1928___ok_15 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v)))) "r") -}} +{{- $_ := ((index $_1928___ok_15 0) | float64) -}} +{{- $ok_15 := (index $_1928___ok_15 1) -}} {{- if $ok_15 -}} {{- $_ := (set $result $k $v) -}} {{- else -}}{{- if (kindIs "bool" $v) -}} @@ -1385,9 +1398,9 @@ {{- $_is_returning := false -}} {{- $result := (dict) -}} {{- range $k, $v := $c -}} -{{- $_1931_b_16_ok_17 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false)))) "r") -}} -{{- $b_16 := (index $_1931_b_16_ok_17 0) -}} -{{- $ok_17 := (index $_1931_b_16_ok_17 1) -}} +{{- $_1948_b_16_ok_17 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false)))) "r") -}} +{{- $b_16 := (index $_1948_b_16_ok_17 0) -}} +{{- $ok_17 := (index $_1948_b_16_ok_17 1) -}} {{- if $ok_17 -}} {{- $_ := (set $result $k $b_16) -}} {{- continue -}} @@ -1430,15 +1443,15 @@ {{- $config := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1976___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1976___hasAccessKey 0) -}} -{{- $hasAccessKey := (index $_1976___hasAccessKey 1) -}} -{{- $_1977___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1977___hasSecretKey 0) -}} -{{- $hasSecretKey := (index $_1977___hasSecretKey 1) -}} -{{- $_1978___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil))))) "r") -}} -{{- $_ := (index $_1978___hasSharedKey 0) -}} -{{- $hasSharedKey := (index $_1978___hasSharedKey 1) -}} +{{- $_1993___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1993___hasAccessKey 0) -}} +{{- $hasAccessKey := (index $_1993___hasAccessKey 1) -}} +{{- $_1994___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1994___hasSecretKey 0) -}} +{{- $hasSecretKey := (index $_1994___hasSecretKey 1) -}} +{{- $_1995___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil))))) "r") -}} +{{- $_ := (index $_1995___hasSharedKey 0) -}} +{{- $hasSharedKey := (index $_1995___hasSharedKey 1) -}} {{- $envvars := (coalesce nil) -}} {{- if (and (not $hasAccessKey) (get (fromJson (include "redpanda.SecretRef.IsValid" (dict "a" (list $tsc.accessKey)))) "r")) -}} {{- $envvars = (concat (default (list) $envvars) (list (mustMergeOverwrite (dict "name" "") (dict "name" "REDPANDA_CLOUD_STORAGE_ACCESS_KEY" "valueFrom" (get (fromJson (include "redpanda.SecretRef.AsSource" (dict "a" (list $tsc.accessKey)))) "r"))))) -}} @@ -1461,12 +1474,12 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_2014___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil))))) "r") -}} -{{- $_ := (index $_2014___containerExists 0) -}} -{{- $containerExists := (index $_2014___containerExists 1) -}} -{{- $_2015___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil))))) "r") -}} -{{- $_ := (index $_2015___accountExists 0) -}} -{{- $accountExists := (index $_2015___accountExists 1) -}} +{{- $_2031___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil))))) "r") -}} +{{- $_ := (index $_2031___containerExists 0) -}} +{{- $containerExists := (index $_2031___containerExists 1) -}} +{{- $_2032___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil))))) "r") -}} +{{- $_ := (index $_2032___accountExists 0) -}} +{{- $accountExists := (index $_2032___accountExists 1) -}} {{- $_is_returning = true -}} {{- (dict "r" (and $containerExists $accountExists)) | toJson -}} {{- break -}} @@ -1477,9 +1490,9 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_2020_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil))))) "r") -}} -{{- $value := (index $_2020_value_ok 0) -}} -{{- $ok := (index $_2020_value_ok 1) -}} +{{- $_2037_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil))))) "r") -}} +{{- $value := (index $_2037_value_ok 0) -}} +{{- $ok := (index $_2037_value_ok 1) -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} diff --git a/charts/redpanda/chart/values.schema.json b/charts/redpanda/chart/values.schema.json index 5d2dc109e..f4437c868 100644 --- a/charts/redpanda/chart/values.schema.json +++ b/charts/redpanda/chart/values.schema.json @@ -4687,6 +4687,9 @@ "enabled": { "type": "boolean" }, + "gateway": { + "type": "boolean" + }, "host": { "type": "string" }, @@ -4884,6 +4887,9 @@ "enabled": { "type": "boolean" }, + "gateway": { + "type": "boolean" + }, "host": { "type": "string" }, @@ -5083,6 +5089,9 @@ "enabled": { "type": "boolean" }, + "gateway": { + "type": "boolean" + }, "host": { "type": "string" }, @@ -5342,6 +5351,9 @@ "enabled": { "type": "boolean" }, + "gateway": { + "type": "boolean" + }, "host": { "type": "string" }, diff --git a/charts/redpanda/secrets.go b/charts/redpanda/secrets.go index 6e54308dc..8c40a0c96 100644 --- a/charts/redpanda/secrets.go +++ b/charts/redpanda/secrets.go @@ -540,7 +540,8 @@ func externalAdvertiseAddress(state *RenderState) string { func advertisedHostJSON(state *RenderState, externalName string, port int32, replicaIndex int) map[string]any { // Gateway API mode: advertise the TLSRoute SNI hostname and the // gateway's advertised port (default 443) rather than a NodePort/LB address. - if state.Values.External.IsGatewayEnabled() { + // Only applies to listeners that opted into gateway mode. + if state.Values.External.IsGatewayEnabled() && isListenerGatewayEnabled(state, externalName) { return advertisedHostJSONGateway(state, externalName, replicaIndex) } @@ -625,6 +626,24 @@ func findListenerHostTemplate(state *RenderState, name string) string { return "" } +// isListenerGatewayEnabled returns true if the named listener has opted into +// Gateway API TLSRoute mode. +func isListenerGatewayEnabled(state *RenderState, name string) bool { + if l, ok := state.Values.Listeners.Kafka.External[name]; ok { + return l.IsGatewayListener() + } + if l, ok := state.Values.Listeners.HTTP.External[name]; ok { + return l.IsGatewayListener() + } + if l, ok := state.Values.Listeners.Admin.External[name]; ok { + return l.IsGatewayListener() + } + if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { + return l.IsGatewayListener() + } + return false +} + // findListenerHost searches all listener types for an external listener with // the given name and returns its Host (bootstrap hostname). func findListenerHost(state *RenderState, name string) string { diff --git a/charts/redpanda/service.gateway.go b/charts/redpanda/service.gateway.go index 26fd5a65c..6ebf28ab6 100644 --- a/charts/redpanda/service.gateway.go +++ b/charts/redpanda/service.gateway.go @@ -104,7 +104,7 @@ func gatewayServicePorts(state *RenderState) []corev1.ServicePort { var ports []corev1.ServicePort for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } ports = append(ports, corev1.ServicePort{ @@ -115,7 +115,7 @@ func gatewayServicePorts(state *RenderState) []corev1.ServicePort { } for name, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } ports = append(ports, corev1.ServicePort{ @@ -126,7 +126,7 @@ func gatewayServicePorts(state *RenderState) []corev1.ServicePort { } for name, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } ports = append(ports, corev1.ServicePort{ @@ -137,7 +137,7 @@ func gatewayServicePorts(state *RenderState) []corev1.ServicePort { } for name, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } ports = append(ports, corev1.ServicePort{ diff --git a/charts/redpanda/service.loadbalancer.go b/charts/redpanda/service.loadbalancer.go index f0cc63955..bf5fca8a8 100644 --- a/charts/redpanda/service.loadbalancer.go +++ b/charts/redpanda/service.loadbalancer.go @@ -27,11 +27,6 @@ func LoadBalancerServices(state *RenderState) []*corev1.Service { return nil } - // Gateway API mode uses its own ClusterIP services and TLSRoutes. - if state.Values.External.IsGatewayEnabled() { - return nil - } - if state.Values.External.Type != corev1.ServiceTypeLoadBalancer { return nil } diff --git a/charts/redpanda/service.nodeport.go b/charts/redpanda/service.nodeport.go index cc2aff4dc..550ec6d9c 100644 --- a/charts/redpanda/service.nodeport.go +++ b/charts/redpanda/service.nodeport.go @@ -24,20 +24,21 @@ func NodePortService(state *RenderState) *corev1.Service { return nil } - // Gateway API mode uses its own ClusterIP services and TLSRoutes. - if state.Values.External.IsGatewayEnabled() { - return nil - } - if state.Values.External.Type != corev1.ServiceTypeNodePort { return nil } + gwEnabled := state.Values.External.IsGatewayEnabled() + var ports []corev1.ServicePort for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { if !listener.IsEnabled() { continue } + // Skip listeners that opted into Gateway API TLSRoute mode. + if gwEnabled && listener.IsGatewayListener() { + continue + } nodePort := listener.Port if len(listener.AdvertisedPorts) > 0 { @@ -56,6 +57,9 @@ func NodePortService(state *RenderState) *corev1.Service { if !listener.IsEnabled() { continue } + if gwEnabled && listener.IsGatewayListener() { + continue + } nodePort := listener.Port if len(listener.AdvertisedPorts) > 0 { @@ -74,6 +78,9 @@ func NodePortService(state *RenderState) *corev1.Service { if !listener.IsEnabled() { continue } + if gwEnabled && listener.IsGatewayListener() { + continue + } nodePort := listener.Port if len(listener.AdvertisedPorts) > 0 { @@ -92,6 +99,9 @@ func NodePortService(state *RenderState) *corev1.Service { if !listener.IsEnabled() { continue } + if gwEnabled && listener.IsGatewayListener() { + continue + } nodePort := listener.Port if len(listener.AdvertisedPorts) > 0 { @@ -106,6 +116,11 @@ func NodePortService(state *RenderState) *corev1.Service { }) } + // If all listeners opted into gateway mode, no NodePort service is needed. + if len(ports) == 0 { + return nil + } + annotations := state.Values.External.Annotations if annotations == nil { annotations = map[string]string{} diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index 707b22ef1..37950b60d 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -106454,6 +106454,1677 @@ spec: # Source: redpanda/templates/entry-point.yaml apiVersion: batch/v1 kind: Job +metadata: + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-delete-policy: before-hook-creation + helm.sh/hook-weight: "-5" + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-configuration + namespace: default +spec: + template: + metadata: + annotations: {} + generateName: redpanda-post- + labels: + app.kubernetes.io/component: redpanda-post-install + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda-configuration + spec: + automountServiceAccountToken: false + containers: + - command: + - /redpanda-operator + - sync-cluster-config + - --users-directory + - /etc/secrets/users + - --redpanda-yaml + - /tmp/base-config/redpanda.yaml + - --bootstrap-yaml + - /tmp/config/.bootstrap.yaml + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: post-install + resources: {} + securityContext: {} + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /tmp/config + name: config + - mountPath: /tmp/base-config + name: base-config + imagePullSecrets: [] + initContainers: + - command: + - /redpanda-operator + - bootstrap + - --in-dir + - /tmp/base-config + - --out-dir + - /tmp/config + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: bootstrap-yaml-envsubst + resources: + limits: + cpu: 100m + memory: 125Mi + requests: + cpu: 100m + memory: 125Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /tmp/config/ + name: config + - mountPath: /tmp/base-config/ + name: base-config + nodeSelector: {} + restartPolicy: Never + securityContext: + fsGroup: 101 + fsGroupChangePolicy: OnRootMismatch + runAsUser: 101 + serviceAccountName: redpanda + tolerations: [] + volumes: + - name: redpanda-default-cert + secret: + defaultMode: 288 + secretName: redpanda-default-cert + - name: redpanda-external-cert + secret: + defaultMode: 288 + secretName: redpanda-external-cert + - configMap: + name: redpanda + name: base-config + - emptyDir: {} + name: config +-- testdata/TestTemplate/gateway-api-migration.yaml.golden -- +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +spec: + maxUnavailable: 1 + selector: + matchLabels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + redpanda.com/poddisruptionbudget: redpanda +--- +# Source: redpanda/charts/console/templates/entry-point.yaml +apiVersion: v1 +automountServiceAccountToken: false +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +automountServiceAccountToken: false +kind: ServiceAccount +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sts-lifecycle + namespace: default +stringData: + common.sh: |- + #!/usr/bin/env bash + + # the SERVICE_NAME comes from the metadata.name of the pod, essentially the POD_NAME + CURL_URL="https://${SERVICE_NAME}.redpanda.default.svc.cluster.local:9644" + + # commands used throughout + CURL_NODE_ID_CMD="curl --silent --fail --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/node_config" + + CURL_MAINTENANCE_DELETE_CMD_PREFIX='curl -X DELETE --silent -o /dev/null -w "%{http_code}"' + CURL_MAINTENANCE_PUT_CMD_PREFIX='curl -X PUT --silent -o /dev/null -w "%{http_code}"' + CURL_MAINTENANCE_GET_CMD="curl -X GET --silent --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/maintenance" + postStart.sh: |- + #!/usr/bin/env bash + # This code should be similar if not exactly the same as that found in the panda-operator, see + # https://github.com/redpanda-data/redpanda/blob/e51d5b7f2ef76d5160ca01b8c7a8cf07593d29b6/src/go/k8s/pkg/resources/secret.go + + # path below should match the path defined on the statefulset + source /var/lifecycle/common.sh + + postStartHook () { + set -x + + touch /tmp/postStartHookStarted + + until NODE_ID=$(${CURL_NODE_ID_CMD} | grep -o '\"node_id\":[^,}]*' | grep -o '[^: ]*$'); do + sleep 0.5 + done + + echo "Clearing maintenance mode on node ${NODE_ID}" + CURL_MAINTENANCE_DELETE_CMD="${CURL_MAINTENANCE_DELETE_CMD_PREFIX} --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/brokers/${NODE_ID}/maintenance" + # a 400 here would mean not in maintenance mode + until [ "${status:-}" = '"200"' ] || [ "${status:-}" = '"400"' ]; do + status=$(${CURL_MAINTENANCE_DELETE_CMD}) + sleep 0.5 + done + + touch /tmp/postStartHookFinished + } + + postStartHook + true + preStop.sh: |- + #!/usr/bin/env bash + # This code should be similar if not exactly the same as that found in the panda-operator, see + # https://github.com/redpanda-data/redpanda/blob/e51d5b7f2ef76d5160ca01b8c7a8cf07593d29b6/src/go/k8s/pkg/resources/secret.go + + touch /tmp/preStopHookStarted + + # path below should match the path defined on the statefulset + source /var/lifecycle/common.sh + + set -x + + preStopHook () { + until NODE_ID=$(${CURL_NODE_ID_CMD} | grep -o '\"node_id\":[^,}]*' | grep -o '[^: ]*$'); do + sleep 0.5 + done + + echo "Setting maintenance mode on node ${NODE_ID}" + CURL_MAINTENANCE_PUT_CMD="${CURL_MAINTENANCE_PUT_CMD_PREFIX} --cacert /etc/tls/certs/default/ca.crt ${CURL_URL}/v1/brokers/${NODE_ID}/maintenance" + until [ "${status:-}" = '"200"' ]; do + status=$(${CURL_MAINTENANCE_PUT_CMD}) + sleep 0.5 + done + + until [ "${finished:-}" = "true" ] || [ "${draining:-}" = "false" ]; do + res=$(${CURL_MAINTENANCE_GET_CMD}) + finished=$(echo $res | grep -o '\"finished\":[^,}]*' | grep -o '[^: ]*$') + draining=$(echo $res | grep -o '\"draining\":[^,}]*' | grep -o '[^: ]*$') + sleep 0.5 + done + + touch /tmp/preStopHookFinished + } + preStopHook + true +type: Opaque +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-configurator + namespace: default +stringData: + configurator.sh: |- + set -xe + SERVICE_NAME=$1 + KUBERNETES_NODE_NAME=$2 + POD_ORDINAL=${SERVICE_NAME##*-} + BROKER_INDEX=`expr $POD_ORDINAL + 1` + + CONFIG=/etc/redpanda/redpanda.yaml + + # Setup config files + cp /tmp/base-config/redpanda.yaml "${CONFIG}" + + LISTENER="{\"address\":\"${SERVICE_NAME}.redpanda.default.svc.cluster.local.\",\"name\":\"internal\",\"port\":9093}" + rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[0] "$LISTENER" + + ADVERTISED_KAFKA_ADDRESSES=() + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30092}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30092}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30092}") + + rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[1] "${ADVERTISED_KAFKA_ADDRESSES[$POD_ORDINAL]}" + + ADVERTISED_KAFKA_ADDRESSES=() + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + + PREFIX_TEMPLATE="" + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + + rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[2] "${ADVERTISED_KAFKA_ADDRESSES[$POD_ORDINAL]}" + + LISTENER="{\"address\":\"${SERVICE_NAME}.redpanda.default.svc.cluster.local.\",\"name\":\"internal\",\"port\":8082}" + rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[0] "$LISTENER" + + ADVERTISED_HTTP_ADDRESSES=() + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") + + PREFIX_TEMPLATE="" + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") + + rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[1] "${ADVERTISED_HTTP_ADDRESSES[$POD_ORDINAL]}" +type: Opaque +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + .bootstrap.json.in: '{"audit_enabled":"false","cloud_storage_cache_size":"5368709120","cloud_storage_enable_remote_read":"true","cloud_storage_enable_remote_write":"true","cloud_storage_enabled":"false","compacted_log_segment_size":"67108864","default_topic_replications":"3","enable_rack_awareness":"false","enable_sasl":"false","kafka_connection_rate_limit":"1000","kafka_enable_authorization":"false","log_segment_size_max":"268435456","log_segment_size_min":"16777216","max_compacted_log_segment_size":"536870912","storage_min_free_bytes":"1073741824"}' + bootstrap.yaml.fixups: '[]' + redpanda.yaml: |- + config_file: /etc/redpanda/redpanda.yaml + pandaproxy: + pandaproxy_api: + - address: 0.0.0.0 + name: internal + port: 8082 + - address: 0.0.0.0 + name: default + port: 8083 + pandaproxy_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + pandaproxy_client: + broker_tls: + enabled: true + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + brokers: + - address: redpanda-0.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-1.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-2.redpanda.default.svc.cluster.local. + port: 9093 + redpanda: + admin: + - address: 0.0.0.0 + name: internal + port: 9644 + - address: 0.0.0.0 + name: default + port: 9645 + admin_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + crash_loop_limit: 5 + empty_seed_starts_cluster: false + kafka_api: + - address: 0.0.0.0 + name: internal + port: 9093 + - address: 0.0.0.0 + name: default + port: 9094 + - address: 0.0.0.0 + name: gw-listener + port: 9095 + kafka_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: gw-listener + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + rpc_server: + address: 0.0.0.0 + port: 33145 + rpc_server_tls: + cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + seed_servers: + - host: + address: redpanda-0.redpanda.default.svc.cluster.local. + port: 33145 + - host: + address: redpanda-1.redpanda.default.svc.cluster.local. + port: 33145 + - host: + address: redpanda-2.redpanda.default.svc.cluster.local. + port: 33145 + rpk: + additional_start_flags: + - --default-log-level=info + - --memory=2048M + - --reserve-memory=205M + - --smp=1 + admin_api: + addresses: + - redpanda-0.redpanda.default.svc.cluster.local.:9644 + - redpanda-1.redpanda.default.svc.cluster.local.:9644 + - redpanda-2.redpanda.default.svc.cluster.local.:9644 + tls: + ca_file: /etc/tls/certs/default/ca.crt + enable_memory_locking: false + kafka_api: + brokers: + - redpanda-0.redpanda.default.svc.cluster.local.:9093 + - redpanda-1.redpanda.default.svc.cluster.local.:9093 + - redpanda-2.redpanda.default.svc.cluster.local.:9093 + tls: + ca_file: /etc/tls/certs/default/ca.crt + overprovisioned: false + schema_registry: + addresses: + - redpanda-0.redpanda.default.svc.cluster.local.:8081 + - redpanda-1.redpanda.default.svc.cluster.local.:8081 + - redpanda-2.redpanda.default.svc.cluster.local.:8081 + tls: + ca_file: /etc/tls/certs/default/ca.crt + tune_aio_events: true + schema_registry: + schema_registry_api: + - address: 0.0.0.0 + name: internal + port: 8081 + - address: 0.0.0.0 + name: default + port: 8084 + schema_registry_api_tls: + - cert_file: /etc/tls/certs/default/tls.crt + enabled: true + key_file: /etc/tls/certs/default/tls.key + name: internal + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + - cert_file: /etc/tls/certs/external/tls.crt + enabled: true + key_file: /etc/tls/certs/external/tls.key + name: default + require_client_auth: false + truststore_file: /etc/tls/certs/external/ca.crt + schema_registry_client: + broker_tls: + enabled: true + require_client_auth: false + truststore_file: /etc/tls/certs/default/ca.crt + brokers: + - address: redpanda-0.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-1.redpanda.default.svc.cluster.local. + port: 9093 + - address: redpanda-2.redpanda.default.svc.cluster.local. + port: 9093 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + profile: |- + admin_api: + addresses: + - redpanda-0:31644 + - redpanda-1:31644 + - redpanda-2:31644 + tls: + ca_file: ca.crt + kafka_api: + brokers: + - redpanda-0:30092 + - redpanda-1:30092 + - redpanda-2:30092 + tls: + ca_file: ca.crt + name: default + schema_registry: + addresses: + - redpanda-0:30081 + - redpanda-1:30081 + - redpanda-2:30081 + tls: + ca_file: ca.crt +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +data: + config.yaml: | + # from .Values.config + kafka: + brokers: + - redpanda-0.redpanda.default.svc.cluster.local.:9093 + - redpanda-1.redpanda.default.svc.cluster.local.:9093 + - redpanda-2.redpanda.default.svc.cluster.local.:9093 + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + redpanda: + adminApi: + enabled: true + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + urls: + - https://redpanda.default.svc.cluster.local.:9644 + schemaRegistry: + enabled: true + tls: + caFilepath: /etc/tls/certs/secrets/redpanda-default-cert/ca.crt + enabled: true + urls: + - https://redpanda-0.redpanda.default.svc.cluster.local.:8081 + - https://redpanda-1.redpanda.default.svc.cluster.local.:8081 + - https://redpanda-2.redpanda.default.svc.cluster.local.:8081 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk-debug-bundle + namespace: default +rules: +- apiGroups: + - "" + resources: + - configmaps + - endpoints + - events + - limitranges + - persistentvolumeclaims + - pods + - pods/log + - replicationcontrollers + - resourcequotas + - serviceaccounts + - services + verbs: + - get + - list +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sidecar + namespace: default +rules: +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-rpk-debug-bundle + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: redpanda-rpk-debug-bundle +subjects: +- kind: ServiceAccount + name: redpanda + namespace: default +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-sidecar + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: redpanda-sidecar +subjects: +- kind: ServiceAccount + name: redpanda + namespace: default +--- +# Source: redpanda/charts/console/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 0 + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external + namespace: default +spec: + externalTrafficPolicy: Local + ports: + - name: admin-default + nodePort: 31644 + port: 9645 + protocol: TCP + targetPort: 0 + - name: kafka-default + nodePort: 30092 + port: 9094 + protocol: TCP + targetPort: 0 + - name: http-default + nodePort: 30082 + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + nodePort: 30081 + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + sessionAffinity: None + type: NodePort +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + monitoring.redpanda.com/enabled: "false" + name: redpanda + namespace: default +spec: + clusterIP: None + ports: + - appProtocol: null + name: admin + port: 9644 + protocol: TCP + targetPort: 9644 + - name: http + port: 8082 + protocol: TCP + targetPort: 8082 + - name: kafka + port: 9093 + protocol: TCP + targetPort: 9093 + - name: rpc + port: 33145 + protocol: TCP + targetPort: 33145 + - name: schemaregistry + port: 8081 + protocol: TCP + targetPort: 8081 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-gateway-bootstrap + namespace: default +spec: + ports: + - name: kafka-gw-listener + port: 9095 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-0 + namespace: default +spec: + ports: + - name: kafka-gw-listener + port: 9095 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-0 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-1 + namespace: default +spec: + ports: + - name: kafka-gw-listener + port: 9095 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-1 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: gw-redpanda-2 + namespace: default +spec: + ports: + - name: kafka-gw-listener + port: 9095 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + statefulset.kubernetes.io/pod-name: redpanda-2 + sessionAffinity: None + type: ClusterIP +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: {} + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: console + app.kubernetes.io/version: v3.7.0 + helm.sh/chart: console-3.7.0 + name: redpanda-console + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + strategy: {} + template: + metadata: + annotations: + checksum/config: 44e632405e10e419e4cb3a5f69d2911edabaa8fd561fc25ec1017dc35a99fc96 + labels: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: console + spec: + affinity: {} + automountServiceAccountToken: false + containers: + - args: + - --config.filepath=/etc/console/configs/config.yaml + command: null + env: + - name: REDPANDA_METRICS_K8S_DEPLOYMENT_TYPE + value: helm + - name: REDPANDA_METRICS_K8S_CHART_VERSION + value: 3.7.0 + - name: REDPANDA_METRICS_K8S_CONSOLE_IMAGE_VERSION + value: redpandadata/console:v3.7.0 + - name: REDPANDA_METRICS_K8S_VERSION + value: v1.99.0-gke + - name: REDPANDA_METRICS_K8S_ENVIRONMENT + value: GCP + envFrom: [] + image: docker.redpanda.com/redpandadata/console:v3.7.0 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /admin/health + port: http + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: console + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /admin/health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/console/configs + name: configs + readOnly: true + - mountPath: /etc/tls/certs + name: redpanda-certificates + imagePullSecrets: [] + initContainers: null + nodeSelector: {} + priorityClassName: "" + securityContext: + fsGroup: 99 + fsGroupChangePolicy: Always + runAsUser: 99 + serviceAccountName: redpanda-console + tolerations: [] + topologySpreadConstraints: [] + volumes: + - configMap: + name: redpanda-console + name: configs + - name: redpanda-certificates + projected: + sources: + - secret: + items: + - key: ca.crt + path: secrets/redpanda-default-cert/ca.crt + name: redpanda-default-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda + namespace: default +spec: + podManagementPolicy: Parallel + replicas: 3 + selector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + serviceName: redpanda + template: + metadata: + annotations: + config.redpanda.com/checksum: f96f9c1fc729be3b095c57ec2682ba0491848d4f356d6b41343427a2e28d484c + labels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + cluster.redpanda.com/broker: "true" + helm.sh/chart: redpanda-26.1.1 + redpanda.com/poddisruptionbudget: redpanda + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + topologyKey: kubernetes.io/hostname + automountServiceAccountToken: false + containers: + - command: + - rpk + - redpanda + - start + - --advertise-rpc-addr=$(SERVICE_NAME).redpanda.default.svc.cluster.local.:33145 + env: + - name: SERVICE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: REDPANDA_METRICS_K8S_VERSION + value: v1.99.0-gke + - name: REDPANDA_METRICS_K8S_DEPLOYMENT_TYPE + value: helm + - name: REDPANDA_METRICS_K8S_CHART_VERSION + value: 26.1.1 + - name: REDPANDA_METRICS_K8S_OPERATOR_IMAGE_VERSION + value: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + - name: REDPANDA_METRICS_K8S_ENVIRONMENT + value: GCP + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + lifecycle: + postStart: + exec: + command: + - bash + - -c + - 'timeout -v 45 bash -x /var/lifecycle/postStart.sh 2>&1 | sed "s/^/lifecycle-hook + post-start $(date): /" | tee /proc/1/fd/1; true' + preStop: + exec: + command: + - bash + - -c + - 'timeout -v 45 bash -x /var/lifecycle/preStop.sh 2>&1 | sed "s/^/lifecycle-hook + pre-stop $(date): /" | tee /proc/1/fd/1; true' + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 9644 + name: redpanda + ports: + - containerPort: 9644 + name: admin + - containerPort: 9645 + name: admin-default + - containerPort: 8082 + name: http + - containerPort: 8083 + name: http-default + - containerPort: 9093 + name: kafka + - containerPort: 9094 + name: kafka-default + - containerPort: 9095 + name: kafka-gw-liste + - containerPort: 33145 + name: rpc + - containerPort: 8081 + name: schemaregistry + - containerPort: 8084 + name: schema-default + resources: + limits: + cpu: 1 + memory: 2.5Gi + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + startupProbe: + exec: + command: + - /bin/sh + - -c + - | + set -e + RESULT=$(curl --silent --fail -k -m 5 --cacert /etc/tls/certs/default/ca.crt "https://${SERVICE_NAME}.redpanda.default.svc.cluster.local.:9644/v1/status/ready") + echo $RESULT + echo $RESULT | grep ready + failureThreshold: 120 + initialDelaySeconds: 1 + periodSeconds: 10 + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /tmp/base-config + name: base-config + - mountPath: /var/lifecycle + name: lifecycle-scripts + - mountPath: /var/lib/redpanda/data + name: datadir + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + - args: + - supervisor + - -- + - /redpanda-operator + - sidecar + - --redpanda-yaml + - /etc/redpanda/redpanda.yaml + - --redpanda-cluster-namespace + - default + - --redpanda-cluster-name + - redpanda + - --selector=helm.sh/chart=redpanda-26.1.1,app.kubernetes.io/name=redpanda,app.kubernetes.io/instance=redpanda + - --run-broker-probe + - --broker-probe-broker-url + - $(SERVICE_NAME).redpanda.default.svc.cluster.local.:9644 + command: + - /redpanda-operator + env: + - name: SERVICE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: sidecar + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 8093 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 0 + resources: {} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true + imagePullSecrets: [] + initContainers: + - command: + - /bin/bash + - -c + - rpk redpanda tune all + env: null + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + name: tuning + resources: {} + securityContext: + capabilities: + add: + - SYS_RESOURCE + privileged: true + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: base-config + - mountPath: /var/lib/redpanda/data + name: datadir + - command: + - /bin/bash + - -c + - trap "exit 0" TERM; exec $CONFIGURATOR_SCRIPT "${SERVICE_NAME}" "${KUBERNETES_NODE_NAME}" + & wait $! + env: + - name: CONFIGURATOR_SCRIPT + value: /etc/secrets/configurator/scripts/configurator.sh + - name: SERVICE_NAME + valueFrom: + configMapKeyRef: null + fieldRef: + fieldPath: metadata.name + resourceFieldRef: null + secretKeyRef: null + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: HOST_IP_ADDRESS + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.hostIP + image: docker.redpanda.com/redpandadata/redpanda:v26.1.1 + name: redpanda-configurator + resources: {} + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + volumeMounts: + - mountPath: /etc/tls/certs/default + name: redpanda-default-cert + - mountPath: /etc/tls/certs/external + name: redpanda-external-cert + - mountPath: /etc/redpanda + name: config + - mountPath: /tmp/base-config + name: base-config + - mountPath: /etc/secrets/configurator/scripts/ + name: redpanda-configurator + - command: + - /redpanda-operator + - bootstrap + - --in-dir + - /tmp/base-config + - --out-dir + - /tmp/config + env: null + image: docker.redpanda.com/redpandadata/redpanda-operator:v26.1.1 + name: bootstrap-yaml-envsubst + resources: + limits: + cpu: 100m + memory: 125Mi + requests: + cpu: 100m + memory: 125Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + volumeMounts: + - mountPath: /tmp/config/ + name: config + - mountPath: /tmp/base-config/ + name: base-config + nodeSelector: {} + priorityClassName: "" + securityContext: + fsGroup: 101 + fsGroupChangePolicy: OnRootMismatch + runAsUser: 101 + serviceAccountName: redpanda + terminationGracePeriodSeconds: 90 + tolerations: [] + topologySpreadConstraints: + - labelSelector: + matchLabels: + app.kubernetes.io/component: redpanda-statefulset + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + volumes: + - name: redpanda-default-cert + secret: + defaultMode: 288 + secretName: redpanda-default-cert + - name: redpanda-external-cert + secret: + defaultMode: 288 + secretName: redpanda-external-cert + - name: lifecycle-scripts + secret: + defaultMode: 509 + secretName: redpanda-sts-lifecycle + - configMap: + name: redpanda + name: base-config + - emptyDir: {} + name: config + - name: redpanda-configurator + secret: + defaultMode: 509 + secretName: redpanda-configurator + - name: datadir + persistentVolumeClaim: + claimName: datadir + - name: kube-api-access + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: + - metadata: + annotations: null + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + name: datadir + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + status: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-root-certificate + namespace: default +spec: + commonName: redpanda-default-root-certificate + duration: 43800h0m0s + isCA: true + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-default-selfsigned-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-default-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-root-certificate + namespace: default +spec: + commonName: redpanda-external-root-certificate + duration: 43800h0m0s + isCA: true + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-external-selfsigned-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-external-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-cert + namespace: default +spec: + dnsNames: + - redpanda-cluster.redpanda.default.svc.cluster.local + - redpanda-cluster.redpanda.default.svc + - redpanda-cluster.redpanda.default + - '*.redpanda-cluster.redpanda.default.svc.cluster.local' + - '*.redpanda-cluster.redpanda.default.svc' + - '*.redpanda-cluster.redpanda.default' + - redpanda.default.svc.cluster.local + - redpanda.default.svc + - redpanda.default + - '*.redpanda.default.svc.cluster.local' + - '*.redpanda.default.svc' + - '*.redpanda.default' + duration: 43800h0m0s + isCA: false + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-default-root-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-default-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-cert + namespace: default +spec: + dnsNames: + - redpanda-cluster.redpanda.default.svc.cluster.local + - redpanda-cluster.redpanda.default.svc + - redpanda-cluster.redpanda.default + - '*.redpanda-cluster.redpanda.default.svc.cluster.local' + - '*.redpanda-cluster.redpanda.default.svc' + - '*.redpanda-cluster.redpanda.default' + - redpanda.default.svc.cluster.local + - redpanda.default.svc + - redpanda.default + - '*.redpanda.default.svc.cluster.local' + - '*.redpanda.default.svc' + - '*.redpanda.default' + duration: 43800h0m0s + isCA: false + issuerRef: + group: cert-manager.io + kind: Issuer + name: redpanda-external-root-issuer + privateKey: + algorithm: ECDSA + size: 256 + secretName: redpanda-external-cert +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-selfsigned-issuer + namespace: default +spec: + selfSigned: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-default-root-issuer + namespace: default +spec: + ca: + secretName: redpanda-default-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-selfsigned-issuer + namespace: default +spec: + selfSigned: {} +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external-root-issuer + namespace: default +spec: + ca: + secretName: redpanda-external-root-certificate +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-gw-listener-bootstrap + namespace: default +spec: + hostnames: + - kafka.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: redpanda-gateway-bootstrap + port: 9095 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-gw-listener-0 + namespace: default +spec: + hostnames: + - kafka-0-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-0 + port: 9095 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-gw-listener-1 + namespace: default +spec: + hostnames: + - kafka-1-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-1 + port: 9095 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-kafka-gw-listener-2 + namespace: default +spec: + hostnames: + - kafka-2-broker.example.com + parentRefs: + - group: null + kind: null + name: kafka-gateway + namespace: null + sectionName: kafka + rules: + - backendRefs: + - name: gw-redpanda-2 + port: 9095 +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: batch/v1 +kind: Job metadata: annotations: helm.sh/hook: post-install,post-upgrade @@ -107138,6 +108809,44 @@ spec: # Source: redpanda/templates/entry-point.yaml apiVersion: v1 kind: Service +metadata: + annotations: {} + labels: + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: redpanda + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: redpanda + helm.sh/chart: redpanda-26.1.1 + name: redpanda-external + namespace: default +spec: + externalTrafficPolicy: Local + ports: + - name: admin-default + nodePort: 31644 + port: 9645 + protocol: TCP + targetPort: 0 + - name: http-default + nodePort: 30082 + port: 8083 + protocol: TCP + targetPort: 0 + - name: schema-default + nodePort: 30081 + port: 8084 + protocol: TCP + targetPort: 0 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/instance: redpanda + app.kubernetes.io/name: redpanda + sessionAffinity: None + type: NodePort +--- +# Source: redpanda/templates/entry-point.yaml +apiVersion: v1 +kind: Service metadata: annotations: {} labels: @@ -107193,22 +108902,10 @@ metadata: namespace: default spec: ports: - - name: admin-default - port: 9645 - protocol: TCP - targetPort: 0 - name: kafka-default port: 9094 protocol: TCP targetPort: 0 - - name: http-default - port: 8083 - protocol: TCP - targetPort: 0 - - name: schema-default - port: 8084 - protocol: TCP - targetPort: 0 publishNotReadyAddresses: true selector: app.kubernetes.io/instance: redpanda @@ -107230,22 +108927,10 @@ metadata: namespace: default spec: ports: - - name: admin-default - port: 9645 - protocol: TCP - targetPort: 0 - name: kafka-default port: 9094 protocol: TCP targetPort: 0 - - name: http-default - port: 8083 - protocol: TCP - targetPort: 0 - - name: schema-default - port: 8084 - protocol: TCP - targetPort: 0 publishNotReadyAddresses: true selector: app.kubernetes.io/instance: redpanda @@ -107268,22 +108953,10 @@ metadata: namespace: default spec: ports: - - name: admin-default - port: 9645 - protocol: TCP - targetPort: 0 - name: kafka-default port: 9094 protocol: TCP targetPort: 0 - - name: http-default - port: 8083 - protocol: TCP - targetPort: 0 - - name: schema-default - port: 8084 - protocol: TCP - targetPort: 0 publishNotReadyAddresses: true selector: app.kubernetes.io/instance: redpanda @@ -107306,22 +108979,10 @@ metadata: namespace: default spec: ports: - - name: admin-default - port: 9645 - protocol: TCP - targetPort: 0 - name: kafka-default port: 9094 protocol: TCP targetPort: 0 - - name: http-default - port: 8083 - protocol: TCP - targetPort: 0 - - name: schema-default - port: 8084 - protocol: TCP - targetPort: 0 publishNotReadyAddresses: true selector: app.kubernetes.io/instance: redpanda diff --git a/charts/redpanda/testdata/template-cases.txtar b/charts/redpanda/testdata/template-cases.txtar index 916c4bdd7..d6a489249 100644 --- a/charts/redpanda/testdata/template-cases.txtar +++ b/charts/redpanda/testdata/template-cases.txtar @@ -1265,6 +1265,44 @@ listeners: external: default: port: 9094 + gateway: true + host: kafka.example.com + hostTemplate: kafka-$POD_ORDINAL-broker.example.com + tls: + cert: default + +-- gateway-api-migration -- +# Gradual migration: existing NodePort listener coexists with a new TLSRoute +# listener on a different port. Verifies that: +# - The NodePort service includes only the traditional listener port (9094) +# - Gateway ClusterIP services include only the TLSRoute listener port (9095) +# - TLSRoutes are created only for the gateway listener +# ASSERT-NO-ERROR +# ASSERT-GOLDEN +external: + enabled: true + type: NodePort + gateway: + enabled: true + parentRefs: + - name: kafka-gateway + sectionName: kafka + advertisedPort: 443 +tls: + enabled: true + certs: + default: + caEnabled: true +listeners: + kafka: + external: + default: + port: 9094 + advertisedPorts: + - 30092 + gw-listener: + port: 9095 + gateway: true host: kafka.example.com hostTemplate: kafka-$POD_ORDINAL-broker.example.com tls: diff --git a/charts/redpanda/tlsroute.go b/charts/redpanda/tlsroute.go index 7641bbc29..be9c05180 100644 --- a/charts/redpanda/tlsroute.go +++ b/charts/redpanda/tlsroute.go @@ -86,7 +86,7 @@ func TLSRoutes(state *RenderState) []*TLSRoute { var routes []*TLSRoute for name, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "kafka", listener.Port) @@ -94,7 +94,7 @@ func TLSRoutes(state *RenderState) []*TLSRoute { } for name, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "http", listener.Port) @@ -102,7 +102,7 @@ func TLSRoutes(state *RenderState) []*TLSRoute { } for name, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "admin", listener.Port) @@ -110,7 +110,7 @@ func TLSRoutes(state *RenderState) []*TLSRoute { } for name, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) { - if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) { + if !ptr.Deref(listener.Enabled, state.Values.External.Enabled) || !listener.IsGatewayListener() { continue } rs := tlsRoutesForListener(fullname, state.Release.Namespace, labels, parentRefs, pods, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), name, "schema", listener.Port) diff --git a/charts/redpanda/values.go b/charts/redpanda/values.go index 105b7921b..b5694a9b3 100644 --- a/charts/redpanda/values.go +++ b/charts/redpanda/values.go @@ -1715,6 +1715,11 @@ func (l *ListenerConfig[T]) ServicePorts(namePrefix string, external *ExternalCo if !ptr.Deref(listener.Enabled, external.Enabled) { continue } + // Skip listeners that opted into Gateway API TLSRoute mode; + // they get their own ClusterIP services instead. + if external.IsGatewayEnabled() && ptr.Deref(listener.Gateway, false) { + continue + } fallbackPorts := append(listener.AdvertisedPorts, l.Port) @@ -1829,13 +1834,18 @@ type ExternalListener[T ~string] struct { AuthenticationMethod *T `json:"authenticationMethod,omitempty"` PrefixTemplate *string `json:"prefixTemplate,omitempty"` - // Host is the SNI hostname used when external.gateway is enabled. - // Used as the hostname in the bootstrap TLSRoute. For per-broker - // TLSRoutes, the pod ordinal is interpolated via HostTemplate. + // Gateway opts this individual listener into Gateway API TLSRoute mode. + // Requires external.gateway to be configured with parentRefs. + // When true, a TLSRoute is created for this listener instead of + // including it in NodePort/LoadBalancer services. This allows gradual + // migration: some listeners can use TLSRoute while others remain on + // NodePort/LoadBalancer. + Gateway *bool `json:"gateway,omitempty"` + // Host is the SNI hostname for the bootstrap TLSRoute (requires gateway: true). Host *string `json:"host,omitempty"` - // HostTemplate is a Go template for per-broker TLSRoute hostnames. + // HostTemplate is a template for per-broker TLSRoute SNI hostnames. // Available variables: $POD_ORDINAL, $POD_NAME. - // Example: "kafka-{{$POD_ORDINAL}}-broker.example.com" + // Example: "kafka-$POD_ORDINAL-broker.example.com" HostTemplate *string `json:"hostTemplate,omitempty"` } @@ -1854,6 +1864,7 @@ func (l *ExternalListener[T]) AsString() ExternalListener[string] { TLS: l.TLS, AuthenticationMethod: auth, PrefixTemplate: l.PrefixTemplate, + Gateway: l.Gateway, Host: l.Host, HostTemplate: l.HostTemplate, } @@ -1868,6 +1879,12 @@ func (l *ExternalListener[T]) IsEnabled() bool { return ptr.Deref(l.Enabled, true) && l.Port > 0 } +// IsGatewayListener returns true when this listener has opted into Gateway API +// TLSRoute mode via the gateway: true field. +func (l *ExternalListener[T]) IsGatewayListener() bool { + return ptr.Deref(l.Gateway, false) +} + type TunableConfig map[string]any // +gotohelm:ignore=true diff --git a/charts/redpanda/values_partial.gen.go b/charts/redpanda/values_partial.gen.go index 1dfba1ee2..bf35ed55c 100644 --- a/charts/redpanda/values_partial.gen.go +++ b/charts/redpanda/values_partial.gen.go @@ -426,6 +426,7 @@ type PartialExternalListener[T ~string] struct { TLS *PartialExternalTLS "json:\"tls,omitempty\"" AuthenticationMethod *T "json:\"authenticationMethod,omitempty\"" PrefixTemplate *string "json:\"prefixTemplate,omitempty\"" + Gateway *bool "json:\"gateway,omitempty\"" Host *string "json:\"host,omitempty\"" HostTemplate *string "json:\"hostTemplate,omitempty\"" } diff --git a/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go b/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go index ba055b6e7..4309c6427 100644 --- a/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go +++ b/operator/api/redpanda/v1alpha2/redpanda_clusterspec_types.go @@ -999,6 +999,8 @@ type ExternalListener struct { // Specifies the network port that the external Service listens on. AdvertisedPorts []int32 `json:"advertisedPorts,omitempty"` NodePort *int32 `json:"nodePort,omitempty"` + // Opts this listener into Gateway API TLSRoute mode. Requires external.gateway to be configured. When true, a TLSRoute is created for this listener instead of including it in NodePort/LoadBalancer services. + Gateway *bool `json:"gateway,omitempty"` // Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. Host *string `json:"host,omitempty"` // HostTemplate is a Go template for per-broker TLSRoute SNI hostnames. Supports $POD_ORDINAL and $POD_NAME variables. diff --git a/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc b/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc index e199a5158..3d2e42e50 100644 --- a/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc +++ b/operator/api/redpanda/v1alpha2/testdata/crd-docs.adoc @@ -1249,6 +1249,7 @@ internal and external listeners. However, it is ignored when specified + on internal listeners. + | | | *`advertisedPorts`* __integer array__ | Specifies the network port that the external Service listens on. + | | | *`nodePort`* __integer__ | | | +| *`gateway`* __boolean__ | Opts this listener into Gateway API TLSRoute mode. Requires external.gateway to be configured. When true, a TLSRoute is created for this listener instead of including it in NodePort/LoadBalancer services. + | | | *`host`* __string__ | Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. + | | | *`hostTemplate`* __string__ | HostTemplate is a Go template for per-broker TLSRoute SNI hostnames. Supports $POD_ORDINAL and $POD_NAME variables. + | | |=== diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go b/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go index 7dd5a97d2..ea6398c5d 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deepcopy.go @@ -1575,6 +1575,11 @@ func (in *ExternalListener) DeepCopyInto(out *ExternalListener) { *out = new(int32) **out = **in } + if in.Gateway != nil { + in, out := &in.Gateway, &out.Gateway + *out = new(bool) + **out = **in + } if in.Host != nil { in, out := &in.Host, &out.Host *out = new(string) diff --git a/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml b/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml index 0cef52790..bcbd37fa4 100644 --- a/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml +++ b/operator/config/crd/bases/cluster.redpanda.com_redpandas.yaml @@ -2041,6 +2041,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -2283,6 +2290,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -2530,6 +2544,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -2860,6 +2881,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -35916,6 +35944,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -36158,6 +36193,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -36405,6 +36447,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. @@ -36735,6 +36784,13 @@ spec: description: Specifies whether this Listener is enabled. type: boolean + gateway: + description: Opts this listener into Gateway API + TLSRoute mode. Requires external.gateway to be + configured. When true, a TLSRoute is created for + this listener instead of including it in NodePort/LoadBalancer + services. + type: boolean host: description: Host is the SNI hostname for the bootstrap TLSRoute when using Gateway API external access. From 746468acabc664e21c250fd85d277d37df20795c Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 20:22:03 -0700 Subject: [PATCH 04/13] charts/redpanda: Rename hostnames to redpanda-broker and add changelog - Rename example hostnames from kafka-*-broker to redpanda-*-broker in test cases and golden files - Add changie changelog entry for the Gateway API TLSRoute feature Co-Authored-By: Claude Opus 4.6 (1M context) --- ...s-redpanda-Added-20260414-gateway-api.yaml | 8 +++++ .../testdata/template-cases.golden.txtar | 34 +++++++++---------- charts/redpanda/testdata/template-cases.txtar | 8 ++--- 3 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 .changes/unreleased/charts-redpanda-Added-20260414-gateway-api.yaml diff --git a/.changes/unreleased/charts-redpanda-Added-20260414-gateway-api.yaml b/.changes/unreleased/charts-redpanda-Added-20260414-gateway-api.yaml new file mode 100644 index 000000000..45d88115e --- /dev/null +++ b/.changes/unreleased/charts-redpanda-Added-20260414-gateway-api.yaml @@ -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 diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index 37950b60d..60cb0e172 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -106740,13 +106740,13 @@ stringData: ADVERTISED_KAFKA_ADDRESSES=() PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-0-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-1-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-2-broker.example.com\",\"name\":\"gw-listener\",\"port\":443}") rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[2] "${ADVERTISED_KAFKA_ADDRESSES[$POD_ORDINAL]}" @@ -108032,7 +108032,7 @@ metadata: namespace: default spec: hostnames: - - kafka.example.com + - redpanda.example.com parentRefs: - group: null kind: null @@ -108058,7 +108058,7 @@ metadata: namespace: default spec: hostnames: - - kafka-0-broker.example.com + - redpanda-0-broker.example.com parentRefs: - group: null kind: null @@ -108084,7 +108084,7 @@ metadata: namespace: default spec: hostnames: - - kafka-1-broker.example.com + - redpanda-1-broker.example.com parentRefs: - group: null kind: null @@ -108110,7 +108110,7 @@ metadata: namespace: default spec: hostnames: - - kafka-2-broker.example.com + - redpanda-2-broker.example.com parentRefs: - group: null kind: null @@ -108398,13 +108398,13 @@ stringData: ADVERTISED_KAFKA_ADDRESSES=() PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") PREFIX_TEMPLATE="" - ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_KAFKA_ADDRESSES+=("{\"address\":\"redpanda-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") rpk redpanda config --config "$CONFIG" set redpanda.advertised_kafka_api[1] "${ADVERTISED_KAFKA_ADDRESSES[$POD_ORDINAL]}" @@ -108414,13 +108414,13 @@ stringData: ADVERTISED_HTTP_ADDRESSES=() PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"kafka-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[1] "${ADVERTISED_HTTP_ADDRESSES[$POD_ORDINAL]}" type: Opaque @@ -109674,7 +109674,7 @@ metadata: namespace: default spec: hostnames: - - kafka.example.com + - redpanda.example.com parentRefs: - group: null kind: null @@ -109700,7 +109700,7 @@ metadata: namespace: default spec: hostnames: - - kafka-0-broker.example.com + - redpanda-0-broker.example.com parentRefs: - group: null kind: null @@ -109726,7 +109726,7 @@ metadata: namespace: default spec: hostnames: - - kafka-1-broker.example.com + - redpanda-1-broker.example.com parentRefs: - group: null kind: null @@ -109752,7 +109752,7 @@ metadata: namespace: default spec: hostnames: - - kafka-2-broker.example.com + - redpanda-2-broker.example.com parentRefs: - group: null kind: null diff --git a/charts/redpanda/testdata/template-cases.txtar b/charts/redpanda/testdata/template-cases.txtar index d6a489249..846b993f0 100644 --- a/charts/redpanda/testdata/template-cases.txtar +++ b/charts/redpanda/testdata/template-cases.txtar @@ -1266,8 +1266,8 @@ listeners: default: port: 9094 gateway: true - host: kafka.example.com - hostTemplate: kafka-$POD_ORDINAL-broker.example.com + host: redpanda.example.com + hostTemplate: redpanda-$POD_ORDINAL-broker.example.com tls: cert: default @@ -1303,7 +1303,7 @@ listeners: gw-listener: port: 9095 gateway: true - host: kafka.example.com - hostTemplate: kafka-$POD_ORDINAL-broker.example.com + host: redpanda.example.com + hostTemplate: redpanda-$POD_ORDINAL-broker.example.com tls: cert: default From ca336a7754e252b73ad33c74f952e07092a316a3 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 20:47:31 -0700 Subject: [PATCH 05/13] charts/redpanda: Fix lint, unit test, and kuttl failures for TLSRoute All three CI failures had the same root cause: the chart's lightweight TLSRoute type was registered in the chart's Scheme but not in the operator's UnifiedScheme. This caused "no kind is registered for the type redpanda.TLSRoute" errors in: - TestFieldManagers / TestFieldManagersRegression (migration tests) - TestControllerRBAC (controller tests) - TestV2ResourceClient (lifecycle tests) - kuttl (operator binary fails to start) Fixes: - Register TLSRoute in the operator's V2Scheme and UnifiedScheme via scheme.go init() - Fix unparam lint: addTLSRouteToScheme no longer returns an always-nil error Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/chart.go | 5 ++--- operator/internal/controller/scheme.go | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/charts/redpanda/chart.go b/charts/redpanda/chart.go index 6a4a1bdcb..a4edd911e 100644 --- a/charts/redpanda/chart.go +++ b/charts/redpanda/chart.go @@ -74,16 +74,15 @@ func init() { must(scheme.AddToScheme(Scheme)) must(certmanagerv1.AddToScheme(Scheme)) must(monitoringv1.AddToScheme(Scheme)) - must(addTLSRouteToScheme(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) error { +func addTLSRouteToScheme(s *runtime.Scheme) { gv := schema.GroupVersion{Group: "gateway.networking.k8s.io", Version: "v1alpha2"} s.AddKnownTypeWithName(gv.WithKind("TLSRoute"), &TLSRoute{}) - return nil } // +gotohelm:ignore=true diff --git a/operator/internal/controller/scheme.go b/operator/internal/controller/scheme.go index 40a1c2111..040c9cdb6 100644 --- a/operator/internal/controller/scheme.go +++ b/operator/internal/controller/scheme.go @@ -14,10 +14,12 @@ import ( monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" mcsv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" + redpanda "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25" redpandav1alpha1 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha1" redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2" vectorizedv1alpha1 "github.com/redpanda-data/redpanda-operator/operator/api/vectorized/v1alpha1" @@ -72,4 +74,11 @@ func init() { for _, fn := range multiclusterSchemeFns { utilruntime.Must(fn(MulticlusterScheme)) } + + // Register the chart's lightweight TLSRoute type so the operator's + // scheme can decode TLSRoute objects rendered by the Helm chart. + tlsRouteGV := schema.GroupVersion{Group: "gateway.networking.k8s.io", Version: "v1alpha2"} + for _, s := range []*runtime.Scheme{V2Scheme, UnifiedScheme} { + s.AddKnownTypeWithName(tlsRouteGV.WithKind("TLSRoute"), &redpanda.TLSRoute{}) + } } From 70a35d3fe66bec5c92afdab3c40103647a2e807b Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 20:58:19 -0700 Subject: [PATCH 06/13] operator: Fix lint and unit test failures - Revert protobuf files to match CI output (buf generate strips license headers that the license updater adds) - Revert zz_generated_status.go import ordering to match CI - Regenerate operator chart golden files to include the new gateway.networking.k8s.io/tlsroutes RBAC rule in all test cases Co-Authored-By: Claude Opus 4.6 (1M context) --- .../testdata/template-cases.golden.txtar | 1224 +++++++++++++++++ .../internal/statuses/zz_generated_status.go | 2 +- .../proto/gen/transport/v1/message.pb.go | 9 - .../proto/gen/transport/v1/message_grpc.pb.go | 9 - 4 files changed, 1225 insertions(+), 19 deletions(-) diff --git a/operator/chart/testdata/template-cases.golden.txtar b/operator/chart/testdata/template-cases.golden.txtar index 2e6d6f1d9..b9a9bd932 100644 --- a/operator/chart/testdata/template-cases.golden.txtar +++ b/operator/chart/testdata/template-cases.golden.txtar @@ -435,6 +435,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -1219,6 +1231,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -1811,6 +1835,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -2622,6 +2658,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -3234,6 +3282,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -4031,6 +4091,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -4787,6 +4859,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -5787,6 +5871,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -6484,6 +6580,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -7282,6 +7390,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -7878,6 +7998,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -8714,6 +8846,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -9398,6 +9542,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -10280,6 +10436,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -10997,6 +11165,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -11810,6 +11990,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -12409,6 +12601,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -13212,6 +13416,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -13813,6 +14029,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -14649,6 +14877,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -15248,6 +15488,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -15888,6 +16140,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -16501,6 +16765,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -17310,6 +17586,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -18085,6 +18373,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -19099,6 +19399,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -19707,6 +20019,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -20395,6 +20719,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -20982,6 +21318,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -21793,6 +22141,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -22400,6 +22760,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -23204,6 +23576,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -24170,6 +24554,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -25189,6 +25585,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -25797,6 +26205,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -26640,6 +27060,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -27397,6 +27829,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -28357,6 +28801,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -29326,6 +29782,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -30279,6 +30747,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -31038,6 +31518,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -32237,6 +32729,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -32844,6 +33348,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -33660,6 +34176,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -34434,6 +34962,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -35557,6 +36097,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -36508,6 +37060,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -37512,6 +38076,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -38530,6 +39106,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -39561,6 +40149,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -40179,6 +40779,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -41195,6 +41807,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -41962,6 +42586,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -43007,6 +43643,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -44296,6 +44944,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -45496,6 +46156,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -46214,6 +46886,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -47049,6 +47733,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -47810,6 +48506,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -49078,6 +49786,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -51399,6 +52119,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -52769,6 +53501,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -54332,6 +55076,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -55442,6 +56198,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -57731,6 +58499,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -59080,6 +59860,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -59797,6 +60589,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -61183,6 +61987,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -62564,6 +63380,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -63664,6 +64492,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -65129,6 +65969,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -66467,6 +67319,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -67205,6 +68069,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -68355,6 +69231,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -69261,6 +70149,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -70653,6 +71553,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -71370,6 +72282,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -72663,6 +73587,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -73383,6 +74319,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -74210,6 +75158,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -74893,6 +75853,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -75720,6 +76692,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -76402,6 +77386,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -77185,6 +78181,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -77777,6 +78785,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -78577,6 +79597,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -79169,6 +80201,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -79952,6 +80996,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -80544,6 +81600,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -81327,6 +82395,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -81919,6 +82999,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -82710,6 +83802,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -83302,6 +84406,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -84093,6 +85209,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -84695,6 +85823,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -85560,6 +86700,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -86253,6 +87405,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -87110,6 +88274,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -87793,6 +88969,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -88707,6 +89895,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -89299,6 +90499,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: @@ -90089,6 +91301,18 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - tlsroutes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - monitoring.coreos.com resources: diff --git a/operator/internal/statuses/zz_generated_status.go b/operator/internal/statuses/zz_generated_status.go index 9b579157a..fad25e6f6 100644 --- a/operator/internal/statuses/zz_generated_status.go +++ b/operator/internal/statuses/zz_generated_status.go @@ -6,12 +6,12 @@ import ( "strings" "time" + "github.com/redpanda-data/common-go/rp-controller-utils/status" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/redpanda-data/common-go/rp-controller-utils/status" redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2" ) diff --git a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go index 91a3cc0f6..78cfcbe54 100644 --- a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go +++ b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message.pb.go @@ -1,12 +1,3 @@ -// Copyright 2026 Redpanda Data, Inc. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.md -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0 - // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 diff --git a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go index 66544b428..33fbe01d2 100644 --- a/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go +++ b/pkg/multicluster/leaderelection/proto/gen/transport/v1/message_grpc.pb.go @@ -1,12 +1,3 @@ -// Copyright 2026 Redpanda Data, Inc. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.md -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0 - // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 From 964fc60cf0add60391b20ca69f9a801e47239101 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 22:25:57 -0700 Subject: [PATCH 07/13] charts/redpanda: Fix TestHelmValuesCompat and gci import ordering - Add omitempty to GatewayParentRef.Name so the zero value is omitted during JSON serialization, matching the generated PartialGatewayParentRef which uses *string with omitempty. Fixes TestHelmValuesCompat fuzz test that detected a round-trip mismatch: CRD emitted {"name":""} but the partial expected {} for an empty GatewayParentRef. - Fix gci import ordering in zz_generated.deprecations_test.go: github.com/redpanda-data/common-go belongs in the third-party group. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/values.go | 2 +- .../api/redpanda/v1alpha2/zz_generated.deprecations_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/charts/redpanda/values.go b/charts/redpanda/values.go index b5694a9b3..46924830b 100644 --- a/charts/redpanda/values.go +++ b/charts/redpanda/values.go @@ -294,7 +294,7 @@ type GatewayParentRef struct { // Kind is the kind of the referent. Defaults to "Gateway". Kind *string `json:"kind,omitempty"` // Name is the name of the referent. - Name string `json:"name"` + Name string `json:"name,omitempty"` // Namespace is the namespace of the referent. When unspecified, refers // to the local namespace of the TLSRoute. Namespace *string `json:"namespace,omitempty"` diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go index 06982f9c4..63f738191 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go @@ -30,12 +30,11 @@ package v1alpha2 import ( "testing" + "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" ) func TestDeprecatedFieldWarnings(t *testing.T) { From 57cf5e26f6b6854e5a435445a8975f4ecd218e36 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 22:32:19 -0700 Subject: [PATCH 08/13] charts/redpanda: Simplify toTLSRouteParentRefs per golangci-lint Use direct type conversion TLSRouteParentRef(ref) instead of field-by-field struct literal, as the linter's unconvert/gocritic fix recommends (identical field sets allow direct conversion). Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/tlsroute.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/charts/redpanda/tlsroute.go b/charts/redpanda/tlsroute.go index be9c05180..1de2661f6 100644 --- a/charts/redpanda/tlsroute.go +++ b/charts/redpanda/tlsroute.go @@ -198,14 +198,7 @@ func tlsRoutesForListener(fullname string, namespace string, labels map[string]s func toTLSRouteParentRefs(refs []GatewayParentRef) []TLSRouteParentRef { var parentRefs []TLSRouteParentRef for _, ref := range refs { - pr := TLSRouteParentRef{ - Name: ref.Name, - Group: ref.Group, - Kind: ref.Kind, - Namespace: ref.Namespace, - SectionName: ref.SectionName, - } - parentRefs = append(parentRefs, pr) + parentRefs = append(parentRefs, TLSRouteParentRef(ref)) } return parentRefs } From 7f4124c431c72442f1da84dd47e4a3d8e65a59dd Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 22:40:59 -0700 Subject: [PATCH 09/13] charts/redpanda: Regenerate _tlsroute.go.tpl after type conversion fix The previous commit simplified toTLSRouteParentRefs to use a direct type conversion but did not regenerate the Helm template. The gotohelm transpiler now emits a simpler template that just copies the ref directly instead of field-by-field merging. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/redpanda/chart/templates/_tlsroute.go.tpl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/charts/redpanda/chart/templates/_tlsroute.go.tpl b/charts/redpanda/chart/templates/_tlsroute.go.tpl index c52bc8c5a..ce80b5b47 100644 --- a/charts/redpanda/chart/templates/_tlsroute.go.tpl +++ b/charts/redpanda/chart/templates/_tlsroute.go.tpl @@ -116,8 +116,7 @@ {{- $_is_returning := false -}} {{- $parentRefs := (coalesce nil) -}} {{- range $_, $ref := $refs -}} -{{- $pr := (mustMergeOverwrite (dict "name" "") (dict "name" $ref.name "group" $ref.group "kind" $ref.kind "namespace" $ref.namespace "sectionName" $ref.sectionName)) -}} -{{- $parentRefs = (concat (default (list) $parentRefs) (list $pr)) -}} +{{- $parentRefs = (concat (default (list) $parentRefs) (list $ref)) -}} {{- end -}} {{- if $_is_returning -}} {{- break -}} From 3ec4d776cf5a73ff81c981104d2d386cef6c7a58 Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 23:09:42 -0700 Subject: [PATCH 10/13] charts/redpanda: Fix TestHelmValuesCompat and golden file null fields Two fixes: 1. TestHelmValuesCompat: The fuzz test generates GatewayParentRef with Name=nil in the partial (*string), but the CRD type uses Name string (always serialized). Add a fixup callback to ensure Name is always non-nil in the fuzz input, matching the CRD's required field behavior. Revert the omitempty addition on both types since Name is required. 2. Golden files: After the toTLSRouteParentRefs type conversion change, the template no longer emits null fields (group, kind, namespace) for parent refs. Regenerated golden files to match. Verified locally: - TestHelmValuesCompat passes 10/10 runs - TestTemplate (all cases) passes - golangci-lint clean Co-Authored-By: Claude Opus 4.6 (1M context) --- .../testdata/template-cases.golden.txtar | 40 ++++--------------- charts/redpanda/values.go | 2 +- .../redpanda/v1alpha2/redpanda_types_test.go | 11 +++++ .../zz_generated.deprecations_test.go | 3 +- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index 60cb0e172..0cbb02380 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -108034,10 +108034,7 @@ spec: hostnames: - redpanda.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -108060,10 +108057,7 @@ spec: hostnames: - redpanda-0-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -108086,10 +108080,7 @@ spec: hostnames: - redpanda-1-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -108112,10 +108103,7 @@ spec: hostnames: - redpanda-2-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -109676,10 +109664,7 @@ spec: hostnames: - redpanda.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -109702,10 +109687,7 @@ spec: hostnames: - redpanda-0-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -109728,10 +109710,7 @@ spec: hostnames: - redpanda-1-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: @@ -109754,10 +109733,7 @@ spec: hostnames: - redpanda-2-broker.example.com parentRefs: - - group: null - kind: null - name: kafka-gateway - namespace: null + - name: kafka-gateway sectionName: kafka rules: - backendRefs: diff --git a/charts/redpanda/values.go b/charts/redpanda/values.go index 46924830b..b5694a9b3 100644 --- a/charts/redpanda/values.go +++ b/charts/redpanda/values.go @@ -294,7 +294,7 @@ type GatewayParentRef struct { // Kind is the kind of the referent. Defaults to "Gateway". Kind *string `json:"kind,omitempty"` // Name is the name of the referent. - Name string `json:"name,omitempty"` + Name string `json:"name"` // Namespace is the namespace of the referent. When unspecified, refers // to the local namespace of the TLSRoute. Namespace *string `json:"namespace,omitempty"` diff --git a/operator/api/redpanda/v1alpha2/redpanda_types_test.go b/operator/api/redpanda/v1alpha2/redpanda_types_test.go index 3cca517a7..c4ba3263f 100644 --- a/operator/api/redpanda/v1alpha2/redpanda_types_test.go +++ b/operator/api/redpanda/v1alpha2/redpanda_types_test.go @@ -149,6 +149,17 @@ func TestHelmValuesCompat(t *testing.T) { // Incorrect type (should be a *resource.Quantity) on an anonymous struct in Partial Values. from.Storage.Tiered.PersistentVolume.Size = nil } + // GatewayParentRef.Name is `string` (not *string) in the Helm chart + // but the partial generator makes it `*string`. Ensure Name is always + // non-nil so the partial's JSON includes "name":"" matching the CRD's + // required field which always serializes. + if from.External != nil && from.External.Gateway != nil { + for i := range from.External.Gateway.ParentRefs { + if from.External.Gateway.ParentRefs[i].Name == nil { + from.External.Gateway.ParentRefs[i].Name = ptr.To("") + } + } + } }) })) } diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go index 63f738191..06982f9c4 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go @@ -30,11 +30,12 @@ package v1alpha2 import ( "testing" - "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" ) func TestDeprecatedFieldWarnings(t *testing.T) { From 937d5b36144954b6519f7fc4b80743cc5a25c1cf Mon Sep 17 00:00:00 2001 From: david-yu Date: Tue, 14 Apr 2026 23:29:27 -0700 Subject: [PATCH 11/13] operator: Fix gci import ordering in zz_generated.deprecations_test.go Previous k8s:generate run reverted the import fix. Reapplying: github.com/redpanda-data/common-go belongs in the third-party group. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/redpanda/v1alpha2/zz_generated.deprecations_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go index 06982f9c4..63f738191 100644 --- a/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go +++ b/operator/api/redpanda/v1alpha2/zz_generated.deprecations_test.go @@ -30,12 +30,11 @@ package v1alpha2 import ( "testing" + "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/redpanda-data/common-go/rp-controller-utils/deprecations" ) func TestDeprecatedFieldWarnings(t *testing.T) { From a25e1aed27a55916ffa7fd880bc49e54a343fb77 Mon Sep 17 00:00:00 2001 From: david-yu Date: Fri, 17 Apr 2026 15:02:45 -0700 Subject: [PATCH 12/13] charts/redpanda: fix gateway advertised listener selection --- .../redpanda/chart/templates/_secrets.go.tpl | 165 +++--------------- charts/redpanda/gateway_advertise_test.go | 53 ++++++ charts/redpanda/secrets.go | 103 ++++------- .../testdata/template-cases.golden.txtar | 6 +- 4 files changed, 107 insertions(+), 220 deletions(-) create mode 100644 charts/redpanda/gateway_advertise_test.go diff --git a/charts/redpanda/chart/templates/_secrets.go.tpl b/charts/redpanda/chart/templates/_secrets.go.tpl index a0d648ed6..530e91351 100644 --- a/charts/redpanda/chart/templates/_secrets.go.tpl +++ b/charts/redpanda/chart/templates/_secrets.go.tpl @@ -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 "") -}} @@ -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 "") -}} @@ -341,17 +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 -}} -{{- if (and (get (fromJson (include "redpanda.ExternalConfig.IsGatewayEnabled" (dict "a" (list $state.Values.external)))) "r") (get (fromJson (include "redpanda.isListenerGatewayEnabled" (dict "a" (list $state $externalName)))) "r")) -}} +{{- 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 $externalName $replicaIndex)))) "r")) | toJson -}} +{{- (dict "r" (get (fromJson (include "redpanda.advertisedHostJSONGateway" (dict "a" (list $state $replicaIndex $host $hostTemplate)))) "r")) | toJson -}} {{- break -}} {{- end -}} -{{- $host := (dict "name" $externalName "address" (get (fromJson (include "redpanda.externalAdvertiseAddress" (dict "a" (list $state)))) "r") "port" $port) -}} +{{- $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)) -}} @@ -361,28 +365,28 @@ 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" $host) | toJson -}} +{{- (dict "r" $hostMap) | toJson -}} {{- break -}} {{- end -}} {{- end -}} {{- define "redpanda.advertisedHostJSONGateway" -}} {{- $state := (index .a 0) -}} -{{- $externalName := (index .a 1) -}} -{{- $replicaIndex := (index .a 2) -}} +{{- $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) -}} -{{- $hostTemplate := (get (fromJson (include "redpanda.findListenerHostTemplate" (dict "a" (list $state $externalName)))) "r") -}} {{- if (eq $hostTemplate "") -}} -{{- $hostTemplate = (get (fromJson (include "redpanda.findListenerHost" (dict "a" (list $state $externalName)))) "r") -}} +{{- $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 -}} @@ -397,136 +401,7 @@ echo "passed"`) -}} {{- end -}} {{- $address := (get (fromJson (include "redpanda.renderBrokerHost" (dict "a" (list $hostTemplate $replicaIndex $podName)))) "r") -}} {{- $_is_returning = true -}} -{{- (dict "r" (dict "name" $externalName "address" $address "port" $port)) | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.findListenerHostTemplate" -}} -{{- $state := (index .a 0) -}} -{{- $name := (index .a 1) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $_619_l_6_ok_7 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_6 := (index $_619_l_6_ok_7 0) -}} -{{- $ok_7 := (index $_619_l_6_ok_7 1) -}} -{{- if $ok_7 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_6.hostTemplate "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_623_l_8_ok_9 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_8 := (index $_623_l_8_ok_9 0) -}} -{{- $ok_9 := (index $_623_l_8_ok_9 1) -}} -{{- if $ok_9 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_8.hostTemplate "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_627_l_10_ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_10 := (index $_627_l_10_ok_11 0) -}} -{{- $ok_11 := (index $_627_l_10_ok_11 1) -}} -{{- if $ok_11 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_10.hostTemplate "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_631_l_12_ok_13 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_12 := (index $_631_l_12_ok_13 0) -}} -{{- $ok_13 := (index $_631_l_12_ok_13 1) -}} -{{- if $ok_13 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_12.hostTemplate "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" "") | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.isListenerGatewayEnabled" -}} -{{- $state := (index .a 0) -}} -{{- $name := (index .a 1) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $_641_l_14_ok_15 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_14 := (index $_641_l_14_ok_15 0) -}} -{{- $ok_15 := (index $_641_l_14_ok_15 1) -}} -{{- if $ok_15 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_14)))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_645_l_16_ok_17 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_16 := (index $_645_l_16_ok_17 0) -}} -{{- $ok_17 := (index $_645_l_16_ok_17 1) -}} -{{- if $ok_17 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_16)))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_649_l_18_ok_19 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_18 := (index $_649_l_18_ok_19 0) -}} -{{- $ok_19 := (index $_649_l_18_ok_19 1) -}} -{{- if $ok_19 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_18)))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_653_l_20_ok_21 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_20 := (index $_653_l_20_ok_21 0) -}} -{{- $ok_21 := (index $_653_l_20_ok_21 1) -}} -{{- if $ok_21 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "redpanda.ExternalListener.IsGatewayListener" (dict "a" (list $l_20)))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" false) | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.findListenerHost" -}} -{{- $state := (index .a 0) -}} -{{- $name := (index .a 1) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $_663_l_22_ok_23 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.kafka.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_22 := (index $_663_l_22_ok_23 0) -}} -{{- $ok_23 := (index $_663_l_22_ok_23 1) -}} -{{- if $ok_23 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_22.host "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_667_l_24_ok_25 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.http.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_24 := (index $_667_l_24_ok_25 0) -}} -{{- $ok_25 := (index $_667_l_24_ok_25 1) -}} -{{- if $ok_25 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_24.host "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_671_l_26_ok_27 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.admin.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_26 := (index $_671_l_26_ok_27 0) -}} -{{- $ok_27 := (index $_671_l_26_ok_27 1) -}} -{{- if $ok_27 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_26.host "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_675_l_28_ok_29 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $state.Values.listeners.schemaRegistry.external $name (dict "enabled" (coalesce nil) "advertisedPorts" (coalesce nil) "port" 0 "nodePort" (coalesce nil) "tls" (coalesce nil)))))) "r") -}} -{{- $l_28 := (index $_675_l_28_ok_29 0) -}} -{{- $ok_29 := (index $_675_l_28_ok_29 1) -}} -{{- if $ok_29 -}} -{{- $_is_returning = true -}} -{{- (dict "r" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l_28.host "")))) "r")) | toJson -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" "") | toJson -}} +{{- (dict "r" (dict "name" "" "address" $address "port" $port)) | toJson -}} {{- break -}} {{- end -}} {{- end -}} diff --git a/charts/redpanda/gateway_advertise_test.go b/charts/redpanda/gateway_advertise_test.go new file mode 100644 index 000000000..52a3f4ef8 --- /dev/null +++ b/charts/redpanda/gateway_advertise_test.go @@ -0,0 +1,53 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +package redpanda + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" + + "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" +) + +func TestAdvertisedHostJSONGatewayUsesCurrentListenerConfig(t *testing.T) { + state := &RenderState{ + Release: &helmette.Release{ + Name: "redpanda", + Namespace: "default", + }, + Values: Values{ + External: ExternalConfig{ + Enabled: true, + Gateway: &GatewayConfig{ + Enabled: true, + AdvertisedPort: ptr.To[int32](8443), + ParentRefs: []GatewayParentRef{{Name: "shared-gateway"}}, + }, + }, + Statefulset: Statefulset{ + Replicas: 2, + }, + }, + } + + host := advertisedHostJSON( + state, + 8082, + 1, + "http.example.com", + "http-$POD_ORDINAL.example.com", + true, + ) + + require.Equal(t, "http-1.example.com", host["address"]) + require.Equal(t, int32(8443), host["port"]) +} diff --git a/charts/redpanda/secrets.go b/charts/redpanda/secrets.go index 8c40a0c96..d203b54c0 100644 --- a/charts/redpanda/secrets.go +++ b/charts/redpanda/secrets.go @@ -398,7 +398,15 @@ func secretConfiguratorKafkaConfig(state *RenderState, sts Statefulset) []string } } - host := advertisedHostJSON(state, externalName, port, replicaIndex) + host := advertisedHostJSON( + state, + port, + replicaIndex, + ptr.Deref(externalVals.Host, ""), + ptr.Deref(externalVals.HostTemplate, ""), + externalVals.IsGatewayListener(), + ) + host["name"] = externalName // XXX: the original code used the stringified `host` value as a template // for re-expansion; however it was impossible to make this work usefully, /// even with the original yaml template. @@ -474,7 +482,15 @@ func secretConfiguratorHTTPConfig(state *RenderState, sts Statefulset) []string } } - host := advertisedHostJSON(state, externalName, port, replicaIndex) + host := advertisedHostJSON( + state, + port, + replicaIndex, + ptr.Deref(externalVals.Host, ""), + ptr.Deref(externalVals.HostTemplate, ""), + externalVals.IsGatewayListener(), + ) + host["name"] = externalName // XXX: the original code used the stringified `host` value as a template // for re-expansion; however it was impossible to make this work usefully, /// even with the original yaml template. @@ -537,16 +553,16 @@ func externalAdvertiseAddress(state *RenderState) string { } // was advertised-host -func advertisedHostJSON(state *RenderState, externalName string, port int32, replicaIndex int) map[string]any { +func advertisedHostJSON(state *RenderState, port int32, replicaIndex int, host string, hostTemplate string, isGateway bool) map[string]any { // Gateway API mode: advertise the TLSRoute SNI hostname and the // gateway's advertised port (default 443) rather than a NodePort/LB address. // Only applies to listeners that opted into gateway mode. - if state.Values.External.IsGatewayEnabled() && isListenerGatewayEnabled(state, externalName) { - return advertisedHostJSONGateway(state, externalName, replicaIndex) + if state.Values.External.IsGatewayEnabled() && isGateway { + return advertisedHostJSONGateway(state, replicaIndex, host, hostTemplate) } - host := map[string]any{ - "name": externalName, + hostMap := map[string]any{ + "name": "", "address": externalAdvertiseAddress(state), "port": port, } @@ -558,35 +574,32 @@ func advertisedHostJSON(state *RenderState, externalName string, port int32, rep address = state.Values.External.Addresses[0] } if domain := ptr.Deref(state.Values.External.Domain, ""); domain != "" { - host = map[string]any{ - "name": externalName, + hostMap = map[string]any{ + "name": "", "address": fmt.Sprintf("%s.%s", address, helmette.Tpl(state.Dot, domain, state.Dot)), "port": port, } } else { - host = map[string]any{ - "name": externalName, + hostMap = map[string]any{ + "name": "", "address": address, "port": port, } } } - return host + return hostMap } // advertisedHostJSONGateway builds the advertised host entry for Gateway API // mode. The address is the per-broker SNI hostname (from HostTemplate) and the // port is the gateway's advertised port (default 443). -func advertisedHostJSONGateway(state *RenderState, externalName string, replicaIndex int) map[string]any { +func advertisedHostJSONGateway(state *RenderState, replicaIndex int, host string, hostTemplate string) map[string]any { gw := state.Values.External.Gateway port := gw.GatewayAdvertisedPort() - // Look up the listener's HostTemplate to build the per-broker address. - // We search all listener types for the matching external name. - hostTemplate := findListenerHostTemplate(state, externalName) if hostTemplate == "" { // Fallback: use the bootstrap host if no template is set. - hostTemplate = findListenerHost(state, externalName) + hostTemplate = host } pods := PodNames(state, Pool{Statefulset: state.Values.Statefulset}) @@ -602,66 +615,12 @@ func advertisedHostJSONGateway(state *RenderState, externalName string, replicaI address := renderBrokerHost(hostTemplate, replicaIndex, podName) return map[string]any{ - "name": externalName, + "name": "", "address": address, "port": port, } } -// findListenerHostTemplate searches all listener types for an external listener -// with the given name and returns its HostTemplate. -func findListenerHostTemplate(state *RenderState, name string) string { - if l, ok := state.Values.Listeners.Kafka.External[name]; ok { - return ptr.Deref(l.HostTemplate, "") - } - if l, ok := state.Values.Listeners.HTTP.External[name]; ok { - return ptr.Deref(l.HostTemplate, "") - } - if l, ok := state.Values.Listeners.Admin.External[name]; ok { - return ptr.Deref(l.HostTemplate, "") - } - if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { - return ptr.Deref(l.HostTemplate, "") - } - return "" -} - -// isListenerGatewayEnabled returns true if the named listener has opted into -// Gateway API TLSRoute mode. -func isListenerGatewayEnabled(state *RenderState, name string) bool { - if l, ok := state.Values.Listeners.Kafka.External[name]; ok { - return l.IsGatewayListener() - } - if l, ok := state.Values.Listeners.HTTP.External[name]; ok { - return l.IsGatewayListener() - } - if l, ok := state.Values.Listeners.Admin.External[name]; ok { - return l.IsGatewayListener() - } - if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { - return l.IsGatewayListener() - } - return false -} - -// findListenerHost searches all listener types for an external listener with -// the given name and returns its Host (bootstrap hostname). -func findListenerHost(state *RenderState, name string) string { - if l, ok := state.Values.Listeners.Kafka.External[name]; ok { - return ptr.Deref(l.Host, "") - } - if l, ok := state.Values.Listeners.HTTP.External[name]; ok { - return ptr.Deref(l.Host, "") - } - if l, ok := state.Values.Listeners.Admin.External[name]; ok { - return ptr.Deref(l.Host, "") - } - if l, ok := state.Values.Listeners.SchemaRegistry.External[name]; ok { - return ptr.Deref(l.Host, "") - } - return "" -} - // adminInternalHTTPProtocol was admin-http-protocol func adminInternalHTTPProtocol(state *RenderState) string { if state.Values.Listeners.Admin.TLS.IsEnabled(&state.Values.TLS) { diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index 0cbb02380..bf618d9ac 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -108402,13 +108402,13 @@ stringData: ADVERTISED_HTTP_ADDRESSES=() PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-0-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-1-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") PREFIX_TEMPLATE="" - ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"redpanda-2-broker.example.com\",\"name\":\"default\",\"port\":9094}") + ADVERTISED_HTTP_ADDRESSES+=("{\"address\":\"${SERVICE_NAME}\",\"name\":\"default\",\"port\":30082}") rpk redpanda config --config "$CONFIG" set pandaproxy.advertised_pandaproxy_api[1] "${ADVERTISED_HTTP_ADDRESSES[$POD_ORDINAL]}" type: Opaque From 075130b2f594b95e29aea8b2adfe8917aac003b1 Mon Sep 17 00:00:00 2001 From: david-yu Date: Fri, 17 Apr 2026 15:03:00 -0700 Subject: [PATCH 13/13] charts/redpanda: validate gateway listener host routing --- .../redpanda/chart/templates/_tlsroute.go.tpl | 7 +- charts/redpanda/tlsroute.go | 6 +- charts/redpanda/tlsroute_test.go | 79 +++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 charts/redpanda/tlsroute_test.go diff --git a/charts/redpanda/chart/templates/_tlsroute.go.tpl b/charts/redpanda/chart/templates/_tlsroute.go.tpl index ce80b5b47..161cd7c4a 100644 --- a/charts/redpanda/chart/templates/_tlsroute.go.tpl +++ b/charts/redpanda/chart/templates/_tlsroute.go.tpl @@ -83,9 +83,10 @@ {{- $_is_returning := false -}} {{- $routes := (coalesce nil) -}} {{- if (eq $host "") -}} -{{- $_is_returning = true -}} -{{- (dict "r" (coalesce nil)) | toJson -}} -{{- break -}} +{{- $_ := (fail (printf "gateway listener %s/%s requires host" $listenerTag $name)) -}} +{{- end -}} +{{- if (and (and (eq $listenerTag "kafka") (gt ((get (fromJson (include "_shims.len" (dict "a" (list $pods)))) "r") | int) (1 | int))) (eq $hostTemplate "")) -}} +{{- $_ := (fail (printf "gateway listener %s/%s requires hostTemplate when replicas > 1" $listenerTag $name)) -}} {{- end -}} {{- $bootstrapSvcName := (printf "%s-gateway-bootstrap" $fullname) -}} {{- $bootstrap := (mustMergeOverwrite (dict "metadata" (dict) "spec" (dict "parentRefs" (coalesce nil) "rules" (coalesce nil))) (mustMergeOverwrite (dict) (dict "apiVersion" "gateway.networking.k8s.io/v1alpha2" "kind" "TLSRoute")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (printf "%s-%s-%s-bootstrap" $fullname $listenerTag $name) "namespace" $namespace "labels" $labels)) "spec" (mustMergeOverwrite (dict "parentRefs" (coalesce nil) "rules" (coalesce nil)) (dict "parentRefs" $parentRefs "hostnames" (list $host) "rules" (list (mustMergeOverwrite (dict) (dict "backendRefs" (list (mustMergeOverwrite (dict "name" "" "port" 0) (dict "name" $bootstrapSvcName "port" $port)))))))))) -}} diff --git a/charts/redpanda/tlsroute.go b/charts/redpanda/tlsroute.go index 1de2661f6..43a09b7fe 100644 --- a/charts/redpanda/tlsroute.go +++ b/charts/redpanda/tlsroute.go @@ -124,7 +124,11 @@ func tlsRoutesForListener(fullname string, namespace string, labels map[string]s var routes []*TLSRoute if host == "" { - return nil + panic(fmt.Sprintf("gateway listener %s/%s requires host", listenerTag, name)) + } + + if listenerTag == "kafka" && len(pods) > 1 && hostTemplate == "" { + panic(fmt.Sprintf("gateway listener %s/%s requires hostTemplate when replicas > 1", listenerTag, name)) } bootstrapSvcName := fmt.Sprintf("%s-gateway-bootstrap", fullname) diff --git a/charts/redpanda/tlsroute_test.go b/charts/redpanda/tlsroute_test.go new file mode 100644 index 000000000..5779148b3 --- /dev/null +++ b/charts/redpanda/tlsroute_test.go @@ -0,0 +1,79 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +package redpanda + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestTLSRoutesForListenerRequiresHost(t *testing.T) { + require.PanicsWithValue(t, + "gateway listener kafka/default requires host", + func() { + tlsRoutesForListener( + "redpanda", + "default", + map[string]string{"app": "redpanda"}, + []TLSRouteParentRef{{Name: "shared-gateway"}}, + []string{"redpanda-0"}, + "", + "", + "default", + "kafka", + 9094, + ) + }, + ) +} + +func TestTLSRoutesForKafkaListenerRequiresHostTemplateForMultiBroker(t *testing.T) { + require.PanicsWithValue(t, + "gateway listener kafka/default requires hostTemplate when replicas > 1", + func() { + tlsRoutesForListener( + "redpanda", + "default", + map[string]string{"app": "redpanda"}, + []TLSRouteParentRef{{Name: "shared-gateway"}}, + []string{"redpanda-0", "redpanda-1"}, + "redpanda.example.com", + "", + "default", + "kafka", + 9094, + ) + }, + ) +} + +func TestTLSRoutesForHTTPListenerAllowsBootstrapOnlyHost(t *testing.T) { + routes := tlsRoutesForListener( + "redpanda", + "default", + map[string]string{"app": "redpanda"}, + []TLSRouteParentRef{{Name: "shared-gateway"}}, + []string{"redpanda-0", "redpanda-1"}, + "proxy.example.com", + "", + "default", + "http", + 8082, + ) + + require.Len(t, routes, 1) + require.Equal(t, metav1.TypeMeta{ + APIVersion: "gateway.networking.k8s.io/v1alpha2", + Kind: "TLSRoute", + }, routes[0].TypeMeta) + require.Equal(t, []string{"proxy.example.com"}, routes[0].Spec.Hostnames) +}