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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: charts/redpanda
kind: Changed
body: The internal Service's `http`, `kafka`, `rpc` and `schemaregistry` ports now render an explicit `appProtocol: null`, and honour `listeners.<api>.appProtocol` when set. Previously only `admin` did. Inert for Kubernetes; the external ports have always rendered the key.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, are you sure about this?

Based on the golden file changes, it seems tha previously admin had appProtocol: null but now it's gone, and the appProtocol: null is missing for other ports.

time: 2026-09-16T16:15:00.000000-04:00
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: charts/redpanda
kind: Changed
body: Every Service port the chart renders now carries `appProtocol` and an explicit `targetPort`. The NodePort and gateway Services previously emitted `targetPort: 0`, which the API server defaults to `port`, and omitted `appProtocol` entirely. Inert to Kubernetes; applying it rewrites the Services.
time: 2026-09-24T14:00:00.000000-04:00
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: charts/redpanda
kind: Fixed
body: The RPC listener's `trustStore` is now projected into the `truststores` volume. `rpc_server_tls.truststore_file` named a path that nothing mounted, so a broker configured with `listeners.rpc.tls.trustStore` failed to start.
time: 2026-09-16T15:55:00.000000-04:00
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: charts/redpanda
kind: Fixed
body: An external listener with `enabled: false` is no longer advertised. The configurator wrote an `advertised_*_api` entry, and the rpk profile an external port, for a listener Redpanda was never told to bind.
time: 2026-09-21T20:16:00.000000-04:00
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: charts/redpanda
kind: Removed
body: '`redpanda.yaml` no longer carries `admin_api_tls`, `kafka_api_tls`, `pandaproxy_api_tls` and `schema_registry_api_tls` as explicit nulls when no listener on that API serves TLS. The keys are now omitted, as `rpc_server_tls` always has been.'
time: 2026-09-21T20:15:00.000000-04:00
4 changes: 4 additions & 0 deletions .changes/unreleased/operator-Fixed-20260916-171500.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: operator
kind: Fixed
body: A StretchCluster listener with `tls.enabled: true` in a pool that disables TLS now gets its certificate. Before, the broker config referenced certificate files that the operator did not issue or mount, and the broker crashlooped.
time: 2026-09-16T17:15:00.000000-04:00
61 changes: 24 additions & 37 deletions charts/redpanda/chart/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const defaultCertDuration = "43800h"

// PKI resolves this chart's values into an authoritative certificate set. When
// possible, use the PKI rather than going through Values.
func PKI(state *RenderState) redpanda.PKI {
func resolvePKI(state *RenderState) redpanda.PKI {
fullname := Fullname(state)
service := ServiceName(state)
ns := state.Release.Namespace
Expand Down Expand Up @@ -156,47 +156,34 @@ func gatewayServerCertDNSNames(state *RenderState, certName string) []string {
return nil
}

pods := gatewayPodNames(state)
// NB: These conditions must agree with resolveAPIListeners.
var names []string
for _, listener := range []ListenerConfig[string]{
state.Values.Listeners.Kafka.AsString(),
state.Values.Listeners.HTTP.AsString(),
state.Values.Listeners.Admin.AsString(),
state.Values.Listeners.SchemaRegistry.AsString(),
} {
for _, external := range helmette.SortedMap(listener.External) {
if !external.IsEnabled() || !ptr.Deref(external.Enabled, state.Values.External.Enabled) {
continue
}

for _, listener := range helmette.SortedMap(state.Values.Listeners.Kafka.External) {
names = appendGatewayCertHosts(state, names, certName, ptr.Deref(listener.Enabled, state.Values.External.Enabled), listener.IsGatewayListener(), listener.TLS, &state.Values.Listeners.Kafka.TLS, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), pods)
}
for _, listener := range helmette.SortedMap(state.Values.Listeners.HTTP.External) {
names = appendGatewayCertHosts(state, names, certName, ptr.Deref(listener.Enabled, state.Values.External.Enabled), listener.IsGatewayListener(), listener.TLS, &state.Values.Listeners.HTTP.TLS, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), pods)
}
for _, listener := range helmette.SortedMap(state.Values.Listeners.Admin.External) {
names = appendGatewayCertHosts(state, names, certName, ptr.Deref(listener.Enabled, state.Values.External.Enabled), listener.IsGatewayListener(), listener.TLS, &state.Values.Listeners.Admin.TLS, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), pods)
}
for _, listener := range helmette.SortedMap(state.Values.Listeners.SchemaRegistry.External) {
names = appendGatewayCertHosts(state, names, certName, ptr.Deref(listener.Enabled, state.Values.External.Enabled), listener.IsGatewayListener(), listener.TLS, &state.Values.Listeners.SchemaRegistry.TLS, ptr.Deref(listener.Host, ""), ptr.Deref(listener.HostTemplate, ""), pods)
}

return names
}
if !external.TLS.IsEnabled(&listener.TLS, &state.Values.TLS) || external.TLS.GetCertName(&listener.TLS) != certName {
continue
}

// appendGatewayCertHosts appends a gateway listener's SNI hostnames to names
// when the listener is enabled, in gateway mode, TLS-enabled, and resolves to
// certName. extTLS.IsEnabled is nil-safe, and GetCertName is only reached when
// it returns true (so extTLS is non-nil there).
func appendGatewayCertHosts(state *RenderState, names []string, certName string, enabled bool, isGateway bool, extTLS *ExternalTLS, listenerTLS *InternalTLS, host string, hostTemplate string, pods []string) []string {
if !enabled || !isGateway {
return names
}
if !extTLS.IsEnabled(listenerTLS, &state.Values.TLS) {
return names
}
if extTLS.GetCertName(listenerTLS) != certName {
return names
}
gateway := resolveGateway(state, external)
if gateway == nil {
continue
}

if host != "" {
names = append(names, host)
}
if hostTemplate != "" {
for i, podname := range pods {
names = append(names, renderBrokerHost(hostTemplate, i, podname))
if gateway.Host != "" {
names = append(names, gateway.Host)
}
names = append(names, gateway.BrokerHosts...)
}
}

return names
}
27 changes: 15 additions & 12 deletions charts/redpanda/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"

consolechart "github.com/redpanda-data/redpanda-operator/charts/console/v3/chart"
"github.com/redpanda-data/redpanda-operator/charts/redpanda/v25"
"github.com/redpanda-data/redpanda-operator/gotohelm"
"github.com/redpanda-data/redpanda-operator/gotohelm/helmette"
)
Expand Down Expand Up @@ -103,9 +104,12 @@ func render(dot *helmette.Dot) []kube.Object {
state.FetchBootstrapUser()
state.FetchStatefulSetPodSelector()

manifests := renderResources(state)
pki := resolvePKI(state)
listeners := resolveListeners(state, &pki)

for _, obj := range StatefulSets(state) {
manifests := renderResources(state, &pki, &listeners)

for _, obj := range StatefulSets(state, &pki, &listeners) {
manifests = append(manifests, obj)
}

Expand All @@ -115,17 +119,17 @@ func render(dot *helmette.Dot) []kube.Object {
return manifests
}

func renderResources(state *RenderState) []kube.Object {
func renderResources(state *RenderState, pki *redpanda.PKI, listeners *redpanda.Listeners) []kube.Object {
state.Values.External.ValidateGateway()
validateGatewayListeners(state)

manifests := []kube.Object{
NodePortService(state),
NodePortService(state, listeners),
PodDisruptionBudget(state),
ServiceAccount(state),
ServiceInternal(state),
ServiceInternal(state, listeners),
ServiceMonitor(state),
PostInstallUpgradeJob(state),
PostInstallUpgradeJob(state, pki),
}

// NB: gotohelm doesn't currently have a way to handle casting from
Expand All @@ -134,7 +138,7 @@ func renderResources(state *RenderState) []kube.Object {
// Instead, it's easiest (though painful to read and write) to iterate over
// all functions that return slices and append them one at a time.

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

Expand All @@ -146,26 +150,25 @@ func renderResources(state *RenderState) []kube.Object {
manifests = append(manifests, obj)
}

pki := PKI(state)
for _, obj := range pki.Render() {
manifests = append(manifests, obj)
}

manifests = append(manifests, RoleSet(state).Render()...)

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

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

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

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

Expand Down
Loading
Loading