This is an automated email from the ASF dual-hosted git repository.
lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-helm-chart.git
The following commit(s) were added to refs/heads/master by this push:
new c8f74d7 Add CI step to validate helm template renders with all
features enabled (#684)
c8f74d7 is described below
commit c8f74d7aa88aea239c549aebc91ba96fcb9e8704
Author: Lari Hotari <[email protected]>
AuthorDate: Mon May 4 15:19:59 2026 +0300
Add CI step to validate helm template renders with all features enabled
(#684)
Adds .ci/templates-all-values.yaml (every optional feature on, with
multi-element lists and maps for every list/map-shaped value) and
.ci/templates-all-values-patch1.yaml (overlay that flips
mutually-exclusive choices: zookeeper -> oxia, JWT symmetric -> OpenID,
selfsigning -> ca issuer, multiVolumes -> single common bookkeeper
volume, hard -> soft anti-affinity, etc.). A new step in the ct-lint
job renders both combinations with `helm template` and validates them
with `kubeconform -strict` against k8s 1.34.0.
This catches indent / `range` / `toYaml | nindent` bugs that no
individual scenario in .ci/clusters/ can surface on its own, since
each existing scenario only enables one or two features at a time.
Also fixes two real bugs uncovered by writing the test:
oxia-coordinator-serviceaccount.yaml and oxia-server-serviceaccount.yaml
treated `images.imagePullSecrets` as an object with `.secretName`,
but per the schema in values.yaml and the `pulsar.imagePullSecrets`
helper, it is a list of secret-name strings. Both files now use the
shared helper and template-error no longer when the field is set.
---
.ci/templates-all-values-patch1.yaml | 172 ++++
.ci/templates-all-values.yaml | 1001 ++++++++++++++++++++
.github/workflows/pulsar-helm-chart-ci.yaml | 51 +
.../templates/oxia-coordinator-serviceaccount.yaml | 5 +-
.../templates/oxia-server-serviceaccount.yaml | 5 +-
5 files changed, 1226 insertions(+), 8 deletions(-)
diff --git a/.ci/templates-all-values-patch1.yaml
b/.ci/templates-all-values-patch1.yaml
new file mode 100644
index 0000000..6723932
--- /dev/null
+++ b/.ci/templates-all-values-patch1.yaml
@@ -0,0 +1,172 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# =============================================================================
+# templates-all-values-patch1.yaml
+# =============================================================================
+# Overlay for `.ci/templates-all-values.yaml`. Each section flips a
+# mutually-exclusive feature so the *other* branch of an `if/else` in the
+# templates is exercised. Apply on top of the base file:
+#
+# helm template charts/pulsar -f .ci/templates-all-values.yaml \
+# -f .ci/templates-all-values-patch1.yaml
+#
+# Note on YAML semantics: helm merges values files at the top level, but
+# *within* a single file YAML keys must be unique -- duplicate top-level
+# keys silently override earlier ones. Keep each top-level key declared
+# exactly once and group all overrides for a component under it.
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Metadata store: Zookeeper -> Oxia
+# Exercises: oxia-coordinator-*.yaml, oxia-server-*.yaml, and the
+# `if and .Values.components.oxia ...` branches in
+# broker-configmap.yaml, broker-statefulset.yaml,
+# proxy-configmap.yaml, autorecovery-configmap.yaml,
+# bookkeeper-cluster-initialize.yaml,
pulsar-cluster-initialize.yaml.
+# -----------------------------------------------------------------------------
+components:
+ zookeeper: false
+ oxia: true
+
+# -----------------------------------------------------------------------------
+# Anti-affinity: hard -> soft
+# Exercises the `else` branch of `eq .Values.<component>.affinity.type
+# "requiredDuringSchedulingIgnoredDuringExecution"` in every
+# StatefulSet / Deployment.
+# -----------------------------------------------------------------------------
+affinity:
+ type: preferredDuringSchedulingIgnoredDuringExecution
+
+# -----------------------------------------------------------------------------
+# Storage: provisioned -> local
+# Exercises the `else if and .Values.volumes.local_storage
+# .Values.<component>.volumes.X.local_storage`
+# storageClassName: "local-storage" branches in bookkeeper-statefulset.yaml
+# and zookeeper-statefulset.yaml volumeClaimTemplates.
+# -----------------------------------------------------------------------------
+volumes:
+ local_storage: true
+
+# -----------------------------------------------------------------------------
+# Zookeeper:
+# - flip statefulsetUpgrade off (exercises the path where
+# zookeeper-statefulset-upgrade.yaml renders nothing)
+# - flip useSeparateDiskForTxlog off (exercises the single-disk branch)
+# - clear storageClassName / storageClass so volumes.local_storage drives
+# the storageClassName: "local-storage" branch
+# -----------------------------------------------------------------------------
+zookeeper:
+ statefulsetUpgrade:
+ enabled: false
+ volumes:
+ useSeparateDiskForTxlog: false
+ data:
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+ datalog:
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+
+# -----------------------------------------------------------------------------
+# Bookkeeper volumes: separate journal/ledgers (with multiVolumes) ->
+# single common volume + index. Cannot coexist with multiVolumes, so
+# explicitly disable those flags.
+# Exercises: bookkeeper-statefulset.yaml `useSingleCommonVolume` branches
+# (volumeMounts + volumeClaimTemplates) and
+# bookkeeper-storageclass.yaml common-volume branch.
+# -----------------------------------------------------------------------------
+bookkeeper:
+ volumes:
+ useSingleCommonVolume: true
+ journal:
+ useMultiVolumes: false
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+ ledgers:
+ useMultiVolumes: false
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+ index:
+ enabled: true
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+ common:
+ name: common
+ size: 60Gi
+ local_storage: true
+ storageClassName: ""
+ storageClass:
+
+# -----------------------------------------------------------------------------
+# Broker: flip statefulsetUpgrade off
+# Exercises: the path where broker-statefulset-upgrade.yaml renders nothing.
+# -----------------------------------------------------------------------------
+broker:
+ statefulsetUpgrade:
+ enabled: false
+
+# -----------------------------------------------------------------------------
+# Cert-manager internal issuer: selfsigning -> ca
+# Exercises: tls-cert-internal-issuer.yaml's unconditional CA Issuer block
+# (the selfsigning Issuer + Certificate block is skipped).
+# Note: when type == "ca" the chart hard-fails if certs.issuers.ca.kind is
+# not "Issuer", so this stays as "Issuer".
+# -----------------------------------------------------------------------------
+certs:
+ internal_issuer:
+ type: ca
+ issuers:
+ ca:
+ name: ca-issuer
+ secretName: ca-secret
+ kind: Issuer
+
+# -----------------------------------------------------------------------------
+# Auth:
+# - JWT (symmetric) -> OpenID Connect (exercises the openid path in
+# broker/proxy/standalone configmaps; jwt-secret-init.yaml renders
+# nothing because JWT is fully off)
+# - drop manager superuser (exercises the `if .auth.superUsers.manager`
+# branches in pulsar-manager-cluster-initialize.yaml /
+# broker-configmap.yaml that omit the manager-token wiring)
+# -----------------------------------------------------------------------------
+auth:
+ authentication:
+ jwt:
+ enabled: false
+ generateSecrets:
+ enabled: false
+ openid:
+ enabled: true
+ openIDAllowedTokenIssuers:
+ - https://issuer-a.example.com/realms/pulsar
+ - https://issuer-b.example.com/realms/pulsar
+ openIDAllowedAudiences:
+ - account
+ - pulsar
+ openIDRoleClaim: sub
+ openIDRequireIssuersUseHttps: "true"
+ superUsers:
+ manager: ""
diff --git a/.ci/templates-all-values.yaml b/.ci/templates-all-values.yaml
new file mode 100644
index 0000000..cff2978
--- /dev/null
+++ b/.ci/templates-all-values.yaml
@@ -0,0 +1,1001 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# =============================================================================
+# templates-all-values.yaml
+# =============================================================================
+# This file is consumed by the "ct-lint" CI job to render the chart with every
+# optional feature turned on, then validate the rendered manifests with
+# kubeconform. The goal is to catch indent/`range`/`toYaml | nindent` bugs in
+# template authoring that wouldn't surface in the existing scenario files,
+# each of which only enables one or two features at a time.
+#
+# Two principles for editing this file:
+# 1. Every feature toggle that has an off-by-default `enabled: true/false`
+# should be set to `true` here (with limited exceptions called out
+# inline -- e.g. the victoria-metrics-k8s-stack subchart).
+# 2. Every value that takes a list or map should contain at least two
+# entries so that nested `range` / `toYaml | nindent` indentation is
+# exercised.
+#
+# Mutually-exclusive choices (Zookeeper vs Oxia, JWT symmetric vs asymmetric,
+# selfsigning vs ca cert issuer, etc.) live in the companion overlay file
+# `.ci/templates-all-values-patch1.yaml`. Apply that overlay on top of this
+# file to exercise the alternative branches.
+#
+# Usage:
+# helm template charts/pulsar -f .ci/templates-all-values.yaml
+# helm template charts/pulsar -f .ci/templates-all-values.yaml \
+# -f .ci/templates-all-values-patch1.yaml
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Components -- enable every optional component
+# Exercises: pulsar-manager-*.yaml, dekaf-*.yaml, broker-rbac.yaml,
+# broker-cluster-role-binding.yaml, jwt-secret-init.yaml
+# -----------------------------------------------------------------------------
+components:
+ zookeeper: true
+ oxia: false # patch1 flips this -> oxia (and zookeeper -> false)
+ bookkeeper: true
+ autorecovery: true
+ broker: true
+ functions: true # exercises function RBAC / serviceaccount paths
+ proxy: true
+ toolset: true
+ pulsar_manager: true # exercises pulsar-manager-*.yaml
+ dekaf: true # exercises dekaf-*.yaml
+
+# -----------------------------------------------------------------------------
+# RBAC -- exercise per-namespace bindings
+# Exercises: broker-rbac.yaml, broker-cluster-role-binding.yaml
+# -----------------------------------------------------------------------------
+rbac:
+ enabled: true
+ limit_to_namespace: true
+
+# -----------------------------------------------------------------------------
+# Anti-affinity -- "hard" mode (patch1 flips to "soft")
+# Exercises the `eq .Values.<component>.affinity.type
+# "requiredDuringSchedulingIgnoredDuringExecution"` branches in every
+# StatefulSet / Deployment.
+# -----------------------------------------------------------------------------
+affinity:
+ anti_affinity: true
+ type: requiredDuringSchedulingIgnoredDuringExecution
+
+# -----------------------------------------------------------------------------
+# Persistence -- on, with non-local storage classes
+# Exercises: bookkeeper-statefulset.yaml volumeClaimTemplates and
+# zookeeper-statefulset.yaml volumeClaimTemplates branches that
+# depend on storageClassName / volumes.local_storage.
+# -----------------------------------------------------------------------------
+persistence: true
+volumes:
+ persistence: true
+ local_storage: false # patch1 flips this -> true (local-storage branch)
+
+# -----------------------------------------------------------------------------
+# Image pull secrets (multi-element list of secret names -- exercises the
+# `pulsar.imagePullSecrets` helper's `range` and the matching iterations in
+# the oxia ServiceAccount templates).
+# -----------------------------------------------------------------------------
+images:
+ imagePullSecrets:
+ - regcred-1
+ - regcred-2
+
+# -----------------------------------------------------------------------------
+# TLS -- enable globally and per-component
+# Exercises: tls-certs-internal.yaml plus the per-component
+# `pulsar.<component>.certs.volumeMounts` / `volumes` includes.
+# -----------------------------------------------------------------------------
+tls:
+ enabled: true
+ proxy:
+ enabled: true
+ broker:
+ enabled: true
+ bookie:
+ enabled: true
+ zookeeper:
+ enabled: true
+ autorecovery:
+ cacerts:
+ enabled: true
+ certs:
+ - name: autorecovery-cacert-a
+ existingSecret: autorecovery-cacert-a
+ secretKeys:
+ - ca.crt
+ - name: autorecovery-cacert-b
+ existingSecret: autorecovery-cacert-b
+ secretKeys:
+ - ca.crt
+ toolset:
+ cacerts:
+ enabled: true
+ certs:
+ - name: toolset-cacert-a
+ existingSecret: toolset-cacert-a
+ secretKeys:
+ - ca.crt
+ - name: toolset-cacert-b
+ existingSecret: toolset-cacert-b
+ secretKeys:
+ - ca.crt
+ function_instance:
+ enabled: true
+ oxia:
+ enabled: true
+ pulsar_metadata:
+ cacerts:
+ enabled: true
+ certs:
+ - name: pulsar-metadata-cacert-a
+ existingSecret: pulsar-metadata-cacert-a
+ secretKeys:
+ - ca.crt
+ - name: pulsar-metadata-cacert-b
+ existingSecret: pulsar-metadata-cacert-b
+ secretKeys:
+ - ca.crt
+
+# -----------------------------------------------------------------------------
+# Cert-manager internal issuer -- selfsigning branch (patch1 flips to "ca")
+# Exercises: tls-cert-internal-issuer.yaml selfsigning Issuer + Certificate
+# -----------------------------------------------------------------------------
+certs:
+ internal_issuer:
+ enabled: true
+ type: selfsigning
+ issuers:
+ selfsigning:
+ name: selfsigned-issuer
+ ca:
+ name: ca-issuer
+ secretName: ca-secret
+ kind: Issuer
+ group: cert-manager.io
+
+# -----------------------------------------------------------------------------
+# Authentication / Authorization -- JWT (symmetric) + multiple super users
+# Exercises: jwt-secret-init.yaml (symmetric branch + range over superUsers
+# and over secretAnnotations.key / .token map).
+# -----------------------------------------------------------------------------
+auth:
+ authentication:
+ enabled: true
+ jwt:
+ enabled: true
+ usingSecretKey: true # symmetric -- patch1 flips to asymmetric
+ generateSecrets:
+ enabled: true
+ nodeSelector:
+ disktype: ssd
+ tier: backend
+ tolerations:
+ - key: jwt-init
+ operator: Equal
+ value: "true"
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ affinity:
+ anti_affinity: true
+ type: requiredDuringSchedulingIgnoredDuringExecution
+ # Two annotation keys on the signing-key secret -- exercises the
+ # `range $k, $v := ...secretAnnotations.key` loop in jwt-secret-init.yaml
+ secretAnnotations:
+ key:
+ reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
+ reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
+ token:
+ # Per-role token annotation maps (multi-element each)
+ broker:
+ reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
+ reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
+ client:
+ reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
+ reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
+ openid:
+ # Disabled here; patch1 disables JWT and enables this instead.
+ enabled: false
+ authorization:
+ enabled: true
+ superUsers:
+ broker: "broker-admin"
+ proxy: "proxy-admin"
+ client: "admin"
+ manager: "manager-admin"
+
+# -----------------------------------------------------------------------------
+# Job TTL -- exercises `if and .Values.job.ttl.enabled` in *-cluster-initialize
+# -----------------------------------------------------------------------------
+job:
+ ttl:
+ enabled: true
+ secondsAfterFinished: 300
+
+# -----------------------------------------------------------------------------
+# extraDeploy -- multi-element list, exercises templates/extra-list.yaml
`range`
+# -----------------------------------------------------------------------------
+extraDeploy:
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: extra-cm-1
+ data:
+ key1: value1
+ key2: value2
+ - apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: extra-cm-2
+ data:
+ hello: world
+
+# -----------------------------------------------------------------------------
+# Zookeeper -- multi-element lists/maps + separate txlog disk
+# Exercises: zookeeper-statefulset.yaml `useSeparateDiskForTxlog` branch and
+# tolerations / nodeSelector / topologySpreadConstraints / labels
+# / annotations indent.
+# -----------------------------------------------------------------------------
+zookeeper:
+ replicaCount: 3
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ metricRelabelings:
+ - action: labeldrop
+ regex: cluster
+ - action: labeldrop
+ regex: pod
+ probe:
+ liveness:
+ enabled: true
+ readiness:
+ enabled: true
+ startup:
+ enabled: true # off by default
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: zookeeper
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-zk-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-zk-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-zk-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-zk-2
+ mountPath: /extra/cm2
+ readOnly: true
+ volumes:
+ useSeparateDiskForTxlog: true
+ persistence: true
+ data:
+ name: data
+ size: 20Gi
+ local_storage: false
+ storageClassName: fast-ssd
+ datalog:
+ name: datalog
+ size: 20Gi
+ local_storage: false
+ storageClassName: fast-ssd
+
+# -----------------------------------------------------------------------------
+# Bookkeeper -- multi-volume journal + ledgers + index
+# Exercises: bookkeeper-statefulset.yaml multi-volume `range` branches plus
+# bookkeeper-configmap.yaml multi-volume directory list rendering.
+# -----------------------------------------------------------------------------
+bookkeeper:
+ replicaCount: 4
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ metricRelabelings:
+ - action: labeldrop
+ regex: cluster
+ - action: labeldrop
+ regex: pod
+ probe:
+ liveness:
+ enabled: true
+ readiness:
+ enabled: true
+ startup:
+ enabled: true # off by default
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: bookie
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-bk-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-bk-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-bk-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-bk-2
+ mountPath: /extra/cm2
+ readOnly: true
+ volumes:
+ persistence: true
+ journal:
+ name: journal
+ size: 10Gi
+ local_storage: false
+ storageClassName: fast-ssd
+ useMultiVolumes: true
+ multiVolumes:
+ - name: journal0
+ size: 10Gi
+ storageClassName: fast-ssd
+ mountPath: /pulsar/data/bookkeeper/journal0
+ - name: journal1
+ size: 10Gi
+ storageClassName: fast-ssd
+ mountPath: /pulsar/data/bookkeeper/journal1
+ ledgers:
+ name: ledgers
+ size: 50Gi
+ local_storage: false
+ storageClassName: fast-ssd
+ useMultiVolumes: true
+ multiVolumes:
+ - name: ledgers0
+ size: 50Gi
+ storageClassName: fast-ssd
+ mountPath: /pulsar/data/bookkeeper/ledgers0
+ - name: ledgers1
+ size: 50Gi
+ storageClassName: fast-ssd
+ mountPath: /pulsar/data/bookkeeper/ledgers1
+ # Dedicated index volume -- exercises bookkeeper-statefulset.yaml
+ # `if .Values.bookkeeper.volumes.index.enabled` branches.
+ index:
+ enabled: true
+ name: index
+ size: 10Gi
+ local_storage: false
+ storageClassName: fast-ssd
+ mountPath: /pulsar/data/bookkeeper/index
+ useSingleCommonVolume: false # patch1 flips this on
+ # Two index directories -- exercises the comma-split init container loop in
+ # bookkeeper-statefulset.yaml when indexDirectories is non-empty.
+ indexDirectories:
+ - /pulsar/data/bookkeeper/index/a
+ - /pulsar/data/bookkeeper/index/b
+
+# -----------------------------------------------------------------------------
+# Autorecovery -- multi-element lists/maps + custom timeout
+# -----------------------------------------------------------------------------
+autorecovery:
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ metricRelabelings:
+ - action: labeldrop
+ regex: cluster
+ - action: labeldrop
+ regex: pod
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: recovery
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-ar-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-ar-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-ar-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-ar-2
+ mountPath: /extra/cm2
+ readOnly: true
+ initContainersExtraVolumeMounts:
+ - name: extra-cm-ar-1
+ mountPath: /init/extra1
+ readOnly: true
+ - name: extra-cm-ar-2
+ mountPath: /init/extra2
+ readOnly: true
+
+# -----------------------------------------------------------------------------
+# pulsar_metadata -- exercise PIP-45 metadata driver toggles + extra init cmd
+# -----------------------------------------------------------------------------
+pulsar_metadata:
+ bookkeeper:
+ usePulsarMetadataClientDriver: true
+ usePulsarMetadataBookieDriver: true
+extraInitCommand: "echo 'extra init command'"
+
+# -----------------------------------------------------------------------------
+# Standalone -- left disabled (mutually exclusive with the distributed setup)
+# -----------------------------------------------------------------------------
+standalone:
+ enabled: false
+
+# -----------------------------------------------------------------------------
+# Broker -- autoscaling + all probes + multi-element lists/maps +
storageOffload
+# Exercises: broker-hpa.yaml (the `if not .Values.broker.autoscaling.enabled`
+# branch in broker-statefulset.yaml line 34 takes the
+# "no-replicas" path), and broker-statefulset.yaml's storageOffload
+# block.
+# -----------------------------------------------------------------------------
+broker:
+ replicaCount: 3
+ autoscaling:
+ enabled: true
+ minReplicas: 1
+ maxReplicas: 5
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ dropUnderscoreCreatedMetrics:
+ enabled: true
+ excludePatterns:
+ - pulsar_topic_load_times_created
+ - pulsar_subscription_back_log_created
+ metricRelabelings:
+ - action: labeldrop
+ regex: cluster
+ - action: labeldrop
+ regex: pod
+ probe:
+ liveness:
+ enabled: true
+ readiness:
+ enabled: true
+ startup:
+ enabled: true # off by default -- exercises the startup probe block
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: broker
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-br-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-br-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-br-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-br-2
+ mountPath: /extra/cm2
+ readOnly: true
+ extraEnvs:
+ - name: BROKER_EXTRA_FOO
+ value: foo
+ - name: BROKER_EXTRA_BAR
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ service:
+ annotations:
+ service.beta.kubernetes.io/aws-load-balancer-scheme: internal
+ service.beta.kubernetes.io/aws-load-balancer-type: nlb
+ service_account:
+ annotations:
+ eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/broker-irsa
+ iam.gke.io/gcp-service-account:
[email protected]
+ # Tiered storage offload block -- exercises broker-statefulset.yaml's
+ # storageOffload mounting and broker-configmap.yaml `range` over keys.
+ storageOffload:
+ driver: aws-s3
+ bucket: pulsar-offload-test
+ region: us-east-1
+ secret: pulsar-offload-aws-creds
+ maxBlockSizeInBytes: "64000000"
+ readBufferSizeInBytes: "1000000"
+ managedLedgerOffloadDeletionLagMs: "14400000"
+ managedLedgerOffloadAutoTriggerSizeThresholdBytes: "-1"
+
+# -----------------------------------------------------------------------------
+# Functions worker (embedded in broker)
+# -----------------------------------------------------------------------------
+functions:
+ useBookieAsStateStore: false
+ rbac:
+ limit_to_namespace: true
+
+# -----------------------------------------------------------------------------
+# Proxy -- all probes + ingress with TLS + multi-element lists/maps
+# Exercises: proxy-ingress.yaml TLS branch, proxy-hpa.yaml is gated by
+# `proxy.autoscaling.enabled` (left disabled here -- exercising
+# that path requires conflicting StatefulSet replicaCount logic
+# and is covered indirectly by other scenarios).
+# -----------------------------------------------------------------------------
+proxy:
+ replicaCount: 3
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ dropUnderscoreCreatedMetrics:
+ enabled: true
+ excludePatterns:
+ - pulsar_proxy_new_connections_created
+ - pulsar_proxy_active_connections_created
+ metricRelabelings:
+ - action: labeldrop
+ regex: cluster
+ - action: labeldrop
+ regex: pod
+ probe:
+ liveness:
+ enabled: true
+ readiness:
+ enabled: true
+ startup:
+ enabled: true # off by default
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: proxy
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-px-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-px-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-px-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-px-2
+ mountPath: /extra/cm2
+ readOnly: true
+ extraEnvs:
+ - name: PROXY_EXTRA_FOO
+ value: foo
+ - name: PROXY_EXTRA_BAR
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ service:
+ type: ClusterIP
+ annotations:
+ service.beta.kubernetes.io/aws-load-balancer-scheme: internal
+ service.beta.kubernetes.io/aws-load-balancer-type: nlb
+ ingress:
+ enabled: true
+ annotations:
+ kubernetes.io/ingress.class: nginx
+ cert-manager.io/cluster-issuer: ca-issuer
+ ingressClassName: nginx
+ tls:
+ enabled: true
+ secretName: proxy-ingress-tls
+ hostname: pulsar.example.com
+ path: /
+ pathType: Prefix
+
+# -----------------------------------------------------------------------------
+# Toolset -- multi-element lists/maps
+# -----------------------------------------------------------------------------
+toolset:
+ useProxy: true
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: toolset
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-ts-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-ts-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-ts-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-ts-2
+ mountPath: /extra/cm2
+ readOnly: true
+
+# -----------------------------------------------------------------------------
+# Oxia -- left disabled here (Zookeeper is the metadata store in the base
+# render). patch1 flips this on. Multi-element ports and tolerations stay
+# defined so the values are exercised when patch1 is layered.
+# Exercises (in patch1): oxia-coordinator-*.yaml, oxia-server-*.yaml.
+# -----------------------------------------------------------------------------
+oxia:
+ initialShardCount: 3
+ replicationFactor: 3
+ coordinator:
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: oxia
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ extraVolumes:
+ - name: extra-cm-ox-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-ox-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-ox-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-ox-2
+ mountPath: /extra/cm2
+ readOnly: true
+ extraContainers:
+ - name: sidecar-exporter
+ image: busybox:1.36
+ command: ["sh", "-c", "while true; do sleep 30; done"]
+ - name: sidecar-tail
+ image: busybox:1.36
+ command: ["sh", "-c", "tail -f /dev/null"]
+ allowExtraAuthorities:
+ - oxia-internal.example.com:6648
+ - oxia-external.example.com:6648
+ server:
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ podMonitor:
+ enabled: true
+ interval: 30s
+ scrapeTimeout: 30s
+ storageClassName: fast-ssd
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: oxia
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+
+# -----------------------------------------------------------------------------
+# Pulsar Manager -- enabled with ingress + admin secret credentials
+# Exercises: pulsar-manager-statefulset.yaml, pulsar-manager-service.yaml,
+# pulsar-manager-ingress.yaml (TLS branch),
+# pulsar-manager-admin-secret.yaml,
+# pulsar-manager-cluster-initialize.yaml.
+# -----------------------------------------------------------------------------
+pulsar_manager:
+ appAnnotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ annotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: pulsar-manager
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: topology.kubernetes.io/zone
+ whenUnsatisfiable: DoNotSchedule
+ - maxSkew: 1
+ topologyKey: kubernetes.io/hostname
+ whenUnsatisfiable: ScheduleAnyway
+ extraVolumes:
+ - name: extra-cm-pm-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-pm-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-pm-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-pm-2
+ mountPath: /extra/cm2
+ readOnly: true
+ service:
+ annotations:
+ service.beta.kubernetes.io/aws-load-balancer-scheme: internal
+ service.beta.kubernetes.io/aws-load-balancer-type: nlb
+ ingress:
+ enabled: true
+ annotations:
+ kubernetes.io/ingress.class: nginx
+ cert-manager.io/cluster-issuer: ca-issuer
+ ingressClassName: nginx
+ tls:
+ enabled: true
+ secretName: pulsar-manager-ingress-tls
+ hostname: pulsar-manager.example.com
+ path: /
+ pathType: Prefix
+ admin:
+ ui_username: pulsar
+ ui_password: change-me # pragma: allowlist secret
+ db_username: pulsar
+ db_password: change-me-db # pragma: allowlist secret
+
+# -----------------------------------------------------------------------------
+# Dekaf UI -- enabled with persistence + multi-element extras
+# Exercises: dekaf-deployment.yaml (extraContainers `range`),
+# dekaf-persistence.yaml, dekaf-service.yaml.
+# -----------------------------------------------------------------------------
+dekaf:
+ deployment:
+ annotations:
+ deploy/owner: pulsar-team
+ deploy/policy: rolling
+ podAnnotations:
+ prometheus.io/scrape: "true"
+ audit.k8s.io/log: "true"
+ nodeSelector:
+ disktype: ssd
+ tier: ui
+ tolerations:
+ - key: dedicated
+ operator: Equal
+ value: dekaf
+ effect: NoSchedule
+ - key: arch
+ operator: Equal
+ value: amd64
+ effect: NoSchedule
+ extraVolumes:
+ - name: extra-cm-dk-1
+ configMap:
+ name: extra-cm-1
+ - name: extra-cm-dk-2
+ configMap:
+ name: extra-cm-2
+ extraVolumeMounts:
+ - name: extra-cm-dk-1
+ mountPath: /extra/cm1
+ readOnly: true
+ - name: extra-cm-dk-2
+ mountPath: /extra/cm2
+ readOnly: true
+ extraContainers:
+ - name: dekaf-sidecar-1
+ image: busybox:1.36
+ command: ["sh", "-c", "while true; do sleep 30; done"]
+ - name: dekaf-sidecar-2
+ image: busybox:1.36
+ command: ["sh", "-c", "tail -f /dev/null"]
+ extraEnv:
+ - name: DEKAF_EXTRA_FOO
+ value: foo
+ - name: DEKAF_EXTRA_BAR
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ persistence:
+ enabled: true
+ storageClass: fast-ssd
+ size: 2Gi
+ # Two custom labels (not "app", which would collide with the standard
+ # label injected by `pulsar.standardLabels`).
+ labels:
+ tier: ui
+ backup-tier: nightly
+ annotations:
+ backup.policy: nightly
+ prometheus.io/scrape: "true"
+ accessModes:
+ - ReadWriteOnce
+ - ReadWriteOncePod
+
+# -----------------------------------------------------------------------------
+# Victoria Metrics subchart -- left disabled here. The subchart's CRDs
+# (VMSingle, VMAgent, VMPodScrape, ...) are not part of the datreeio CRDs
+# catalog used by kubeconform, so enabling it would produce schema-validation
+# failures unrelated to the chart we're testing. With the subchart off, the
+# chart's own *-podmonitor.yaml templates emit standard
+# `monitoring.coreos.com/v1/PodMonitor` objects, which are in the catalog.
+# -----------------------------------------------------------------------------
+victoria-metrics-k8s-stack:
+ enabled: false
+ victoria-metrics-operator:
+ enabled: false
+ vmsingle:
+ enabled: false
+ vmagent:
+ enabled: false
+ vmalert:
+ enabled: false
+ alertmanager:
+ enabled: false
+ grafana:
+ enabled: false
+ prometheus-node-exporter:
+ enabled: false
+ kube-state-metrics:
+ enabled: false
+ kubelet:
+ enabled: false
+ kubeApiServer:
+ enabled: false
+ kubeControllerManager:
+ enabled: false
+ coreDns:
+ enabled: false
+ kubeEtcd:
+ enabled: false
+ kubeScheduler:
+ enabled: false
diff --git a/.github/workflows/pulsar-helm-chart-ci.yaml
b/.github/workflows/pulsar-helm-chart-ci.yaml
index eff01f5..83c2192 100644
--- a/.github/workflows/pulsar-helm-chart-ci.yaml
+++ b/.github/workflows/pulsar-helm-chart-ci.yaml
@@ -166,6 +166,57 @@ jobs:
done
done
+ - name: Validate "all features enabled" helm template renders cleanly
+ # Renders the chart with .ci/templates-all-values.yaml (every optional
+ # feature on, multi-element lists/maps everywhere) and the patch1
+ # overlay (mutually-exclusive flips: zookeeper -> oxia, JWT symmetric
+ # -> OpenID, selfsigning issuer -> CA issuer, multiVolumes -> single
+ # common volume, hard -> soft anti-affinity, ...). Catches indent /
+ # `range` / `toYaml | nindent` bugs that no individual scenario file
+ # in .ci/clusters/ would surface on its own.
+ if: ${{ steps.check_changes.outputs.docs_only != 'true' }}
+ run: |
+ PULSAR_CHART_HOME=$(pwd)
+ source ${PULSAR_CHART_HOME}/hack/common.sh
+ source ${PULSAR_CHART_HOME}/.ci/helm.sh
+ hack::ensure_kubectl
+ hack::ensure_helm
+ hack::ensure_kubeconform
+ ci::helm_repo_add
+ helm dependency build charts/pulsar
+ set -o pipefail
+ kube_version=1.34.0
+
schema_crd_url='https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
+ render_dir="$RUNNER_TEMP/templates-all-values"
+ render_dir_patch1="$RUNNER_TEMP/templates-all-values-patch1"
+ rm -rf "$render_dir" "$render_dir_patch1"
+ mkdir -p "$render_dir" "$render_dir_patch1"
+
+ echo "::group::helm template (.ci/templates-all-values.yaml)"
+ helm template charts/pulsar \
+ --kube-version $kube_version \
+ --values .ci/templates-all-values.yaml \
+ --output-dir "$render_dir"
+ echo "::endgroup::"
+
+ echo "::group::kubeconform validate (base)"
+ kubeconform -schema-location default -schema-location
"$schema_crd_url" \
+ -strict -kubernetes-version $kube_version -summary "$render_dir"
+ echo "::endgroup::"
+
+ echo "::group::helm template (.ci/templates-all-values.yaml +
patch1)"
+ helm template charts/pulsar \
+ --kube-version $kube_version \
+ --values .ci/templates-all-values.yaml \
+ --values .ci/templates-all-values-patch1.yaml \
+ --output-dir "$render_dir_patch1"
+ echo "::endgroup::"
+
+ echo "::group::kubeconform validate (patch1)"
+ kubeconform -schema-location default -schema-location
"$schema_crd_url" \
+ -strict -kubernetes-version $kube_version -summary
"$render_dir_patch1"
+ echo "::endgroup::"
+
- name: Validate kustomize yaml for extra new lines in pulsar-init
commands
if: ${{ steps.check_changes.outputs.docs_only != 'true' }}
run: |
diff --git a/charts/pulsar/templates/oxia-coordinator-serviceaccount.yaml
b/charts/pulsar/templates/oxia-coordinator-serviceaccount.yaml
index fbed004..f9cc2b9 100644
--- a/charts/pulsar/templates/oxia-coordinator-serviceaccount.yaml
+++ b/charts/pulsar/templates/oxia-coordinator-serviceaccount.yaml
@@ -29,8 +29,5 @@ metadata:
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
-{{- if .Values.images.imagePullSecrets }}
-imagePullSecrets:
- - name: {{ .Values.images.imagePullSecrets.secretName }}
-{{- end}}
+{{ include "pulsar.imagePullSecrets" . }}
{{- end}}
\ No newline at end of file
diff --git a/charts/pulsar/templates/oxia-server-serviceaccount.yaml
b/charts/pulsar/templates/oxia-server-serviceaccount.yaml
index 3158a9d..37072c3 100644
--- a/charts/pulsar/templates/oxia-server-serviceaccount.yaml
+++ b/charts/pulsar/templates/oxia-server-serviceaccount.yaml
@@ -29,8 +29,5 @@ metadata:
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
-{{- if .Values.images.imagePullSecrets }}
-imagePullSecrets:
- - name: {{ .Values.images.imagePullSecrets.secretName }}
-{{- end}}
+{{ include "pulsar.imagePullSecrets" . }}
{{- end}}
\ No newline at end of file