Skip to content
Merged
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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ jobs:

# Without its own tolerations the API pod waits out Kubernetes' default
# 300 s on a lost node. A user's tolerations must not drop them.
- name: Assert cnpg.enabled replaces the database
run: |
on=(--set cnpg.enabled=true --set postgresql.enabled=false --api-versions postgresql.cnpg.io/v1)
out=$(helm template kates charts/kates/ "${on[@]}")
cluster=$(printf '%s\n' "$out" | yq 'select(.kind == "Cluster")')
[ "$(printf '%s\n' "$cluster" | yq .spec.instances)" = 3 ]
[ "$(printf '%s\n' "$cluster" | yq .spec.affinity.podAntiAffinityType)" = required ]
[ "$(printf '%s\n' "$cluster" | yq .spec.postgresql.synchronous.dataDurability)" = preferred ]
[ "$(printf '%s\n' "$cluster" | yq .spec.bootstrap.initdb.database)" = kates ]
if printf '%s\n' "$out" | grep -q '^kind: StatefulSet'; then exit 1; fi
printf '%s\n' "$out" | grep -q 'jdbc:postgresql://kates-db-rw\.'
printf '%s\n' "$out" | grep -A3 'name: QUARKUS_DATASOURCE_PASSWORD' | grep -q 'name: kates-db-app'
printf '%s\n' "$out" | grep -q 'until nc -z kates-db-rw\.'
printf '%s\n' "$out" | grep -q 'cnpg.io/cluster: kates-db'
# One instance has no standby to wait for.
if helm template kates charts/kates/ "${on[@]}" --set cnpg.instances=1 | grep -q synchronous:; then exit 1; fi
# Refused: two databases, and no operator.
if helm template kates charts/kates/ --set cnpg.enabled=true --api-versions postgresql.cnpg.io/v1 >/dev/null 2>&1; then exit 1; fi
if helm template kates charts/kates/ --set cnpg.enabled=true --set postgresql.enabled=false >/dev/null 2>&1; then exit 1; fi

- name: Assert the API pod leaves a lost node quickly
run: |
out=$(helm template kates charts/kates/ \
Expand Down
6 changes: 4 additions & 2 deletions charts/kates-platform/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: kates-platform
description: Umbrella chart for the full Kates platform — Kafka, Kates, and supporting infrastructure
icon: https://raw.githubusercontent.com/bmscomp/kates/main/docs/assets/charts/kates-platform.svg
type: application
# Patch: the umbrella packs kates 0.16.0, kafka-cluster 1.0.31,
# Patch: the umbrella packs kates 0.17.0, kafka-cluster 1.0.31,
# connect-cluster 2.1.26, mirror-maker2 0.11.26.
version: 0.8.26
version: 0.8.27
appVersion: "1.0.0"
keywords:
- kafka
Expand Down Expand Up @@ -42,6 +42,8 @@ dependencies:
condition: connect-cluster.enabled
annotations:
artifacthub.io/changes: |
- kind: changed
description: "Packs kates 0.17.0, with the opt-in CloudNativePG database (kates.cnpg.enabled). Templates are unchanged."
- kind: changed
description: "Packs kates 0.16.0, whose API pod is replaced 30 s after its node is lost (nodeFailureTolerationSeconds). Templates are unchanged."
- kind: changed
Expand Down
6 changes: 4 additions & 2 deletions charts/kates/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ apiVersion: v2
name: kates
description: Kates — Kafka Advanced Testing & Engineering Suite
type: application
# Minor: nodeFailureTolerationSeconds, a new value.
version: 0.16.0
# Minor: cnpg, a new value block.
version: 0.17.0
# The non-blocking health endpoints this chart's probes now point at come from
# a BUILD-TIME property (quarkus.messaging.health.enabled=false), so they only
# exist in the image built from this branch. Keep appVersion and
Expand All @@ -27,6 +27,8 @@ annotations:
artifacthub.io/category: integration-delivery
artifacthub.io/license: Apache-2.0
artifacthub.io/changes: |
- kind: added
description: "cnpg.enabled renders a CloudNativePG Cluster and connects the API to its primary, so the database survives a node crash. It needs the operator installed first and postgresql.enabled=false. Off by default."
- kind: added
description: "nodeFailureTolerationSeconds, default 30: the API pod is replaced 30 s after its node goes NotReady or unreachable, not the 300 s Kubernetes defaults to. A user's tolerations are kept beside these two."
- kind: changed
Expand Down
5 changes: 5 additions & 0 deletions charts/kates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ Upgrading from 0.10.7 or earlier: `values-prod.yaml` there ran an autoscaler of
| `postgresql.enabled` | `true` | Deploy bundled PostgreSQL |
| `postgresql.auth.existingSecret` | `""` | Pre-created Secret holding the DB password (the username is `postgresql.auth.username`) |
| `postgresql.auth.passwordKey` | `password` | Key in that Secret holding the password |
| `cnpg.enabled` | `false` | Replicated PostgreSQL through CloudNativePG; needs the operator and `postgresql.enabled=false` |
| `cnpg.instances` | `3` | PostgreSQL instances, each on its own node |
| `cnpg.storage.size` | `10Gi` | Volume size of each instance |
| `cnpg.storage.storageClass` | `""` | Storage class; the cluster's default when empty |
| `cnpg.resources` | see `values.yaml` | Requests and limits of each instance |
| `externalDatabase.enabled` | `false` | Use an external PostgreSQL |
| `externalDatabase.existingSecret` | `""` | Pre-created Secret holding the external DB password (the username is `externalDatabase.username`) |
| `externalDatabase.passwordKey` | `password` | Key in that Secret holding the password |
Expand Down
2 changes: 1 addition & 1 deletion charts/kates/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
Status:
RBAC: {{ if .Values.rbac.create }}enabled{{ else }}disabled{{ end }}
Engine: {{ .Values.engine.defaultBackend }}
Database: {{ if .Values.postgresql.enabled }}bundled PostgreSQL{{ else if .Values.externalDatabase.enabled }}external{{ else }}none{{ end }}
Database: {{ if .Values.postgresql.enabled }}bundled PostgreSQL{{ else if .Values.cnpg.enabled }}CloudNativePG{{ else if .Values.externalDatabase.enabled }}external{{ else }}none{{ end }}
Replicas: {{ if .Values.autoscaling.enabled }}auto ({{ .Values.autoscaling.minReplicas }}-{{ .Values.autoscaling.maxReplicas }}){{ else }}{{ .Values.replicaCount }}{{ end }}

{{- if .Values.metrics.serviceMonitor.enabled }}
Expand Down
23 changes: 20 additions & 3 deletions charts/kates/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,31 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{ include "kates.fullname" . }}-postgresql
{{- end }}

{{- define "kates.cnpg.fullname" -}}
{{ include "kates.fullname" . }}-db
{{- end }}

{{/* The host the chart waits for: the bundled database's, or the primary's Service. */}}
{{- define "kates.dbHost" -}}
{{- $name := ternary (printf "%s-rw" (include "kates.cnpg.fullname" .)) (include "kates.postgresql.fullname" .) .Values.cnpg.enabled -}}
{{ $name }}.{{ .Release.Namespace }}.svc.{{ .Values.global.clusterDomain }}
{{- end }}

{{- define "kates.postgresql.jdbcUrl" -}}
{{- if .Values.postgresql.enabled -}}
jdbc:postgresql://{{ include "kates.postgresql.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.global.clusterDomain }}:5432/{{ .Values.postgresql.auth.database }}
{{- else if .Values.cnpg.enabled -}}
{{- /* Timeouts: a connection to a dead node otherwise hangs on TCP for minutes after a failover. */ -}}
jdbc:postgresql://{{ include "kates.dbHost" . }}:5432/{{ .Values.externalDatabase.database }}?socketTimeout=60&tcpKeepAlive=true&connectTimeout=5
{{- else -}}
jdbc:postgresql://{{ .Values.externalDatabase.host }}:{{ .Values.externalDatabase.port | default 5432 }}/{{ .Values.externalDatabase.database }}
{{- end -}}
{{- end }}

{{- define "kates.dbSecretName" -}}
{{- if .Values.postgresql.enabled -}}
{{- if .Values.cnpg.enabled -}}
{{- include "kates.cnpg.fullname" . -}}-app
{{- else if .Values.postgresql.enabled -}}
{{- if .Values.postgresql.auth.existingSecret -}}
{{- .Values.postgresql.auth.existingSecret -}}
{{- else -}}
Expand All @@ -74,7 +89,9 @@ postgresql.auth.username or externalDatabase.username.
*/}}
{{- define "kates.dbPasswordKey" -}}
{{- $db := ternary .Values.postgresql.auth .Values.externalDatabase .Values.postgresql.enabled -}}
{{- if $db.existingSecret -}}
{{- if .Values.cnpg.enabled -}}
password
{{- else if $db.existingSecret -}}
{{- $db.passwordKey | default "password" -}}
{{- else -}}
QUARKUS_DATASOURCE_PASSWORD
Expand All @@ -91,7 +108,7 @@ migration Job. The Secret is required: a wrong name or key stops the pod
with CreateContainerConfigError instead of logging in with no password.
*/}}
{{- define "kates.dbEnv" -}}
{{- if or .Values.postgresql.enabled .Values.externalDatabase.enabled -}}
{{- if or .Values.postgresql.enabled .Values.externalDatabase.enabled .Values.cnpg.enabled -}}
- name: QUARKUS_DATASOURCE_USERNAME
value: {{ include "kates.dbUsername" . | quote }}
- name: QUARKUS_DATASOURCE_PASSWORD
Expand Down
49 changes: 49 additions & 0 deletions charts/kates/templates/cnpg-cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{{- if .Values.cnpg.enabled }}
{{- if or .Values.postgresql.enabled .Values.externalDatabase.enabled }}
{{- fail "cnpg.enabled replaces the database: set postgresql.enabled=false and externalDatabase.enabled=false." }}
{{- end }}
{{- if not (.Capabilities.APIVersions.Has "postgresql.cnpg.io/v1") }}
{{- fail "cnpg.enabled needs the CloudNativePG operator: install it first, see the deployment chapter of the book. helm template needs --api-versions postgresql.cnpg.io/v1." }}
{{- end }}
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: {{ include "kates.cnpg.fullname" . }}
labels:
{{- include "kates.labels" . | nindent 4 }}
app.kubernetes.io/component: database
spec:
instances: {{ .Values.cnpg.instances }}
primaryUpdateStrategy: unsupervised
inheritedMetadata:
labels:
app.kubernetes.io/name: {{ include "kates.cnpg.fullname" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: database
affinity:
enablePodAntiAffinity: true
podAntiAffinityType: required
topologyKey: kubernetes.io/hostname
{{- if gt (int .Values.cnpg.instances) 1 }}
postgresql:
# preferred: with no standby left the primary keeps writing, so a Kates API
# never stalls on its database. A double failure can lose the last writes.
synchronous:
method: any
number: 1
dataDurability: preferred
{{- end }}
bootstrap:
initdb:
database: {{ .Values.externalDatabase.database }}
owner: {{ include "kates.dbUsername" . }}
storage:
size: {{ .Values.cnpg.storage.size }}
{{- with .Values.cnpg.storage.storageClass }}
storageClass: {{ . | quote }}
{{- end }}
{{- with .Values.cnpg.resources }}
resources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
2 changes: 1 addition & 1 deletion charts/kates/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ data:
KATES_ENGINE_SUBMISSION_TIMEOUT_MS: {{ .Values.engine.submissionTimeoutMs | default "120000" | quote }}
KATES_API_SECURITY_ENABLED: {{ .Values.apiKey.enabled | default false | quote }}

{{- if or .Values.postgresql.enabled .Values.externalDatabase.enabled }}
{{- if or .Values.postgresql.enabled .Values.externalDatabase.enabled .Values.cnpg.enabled }}
QUARKUS_DATASOURCE_DB_KIND: "postgresql"
QUARKUS_DATASOURCE_JDBC_URL: {{ include "kates.postgresql.jdbcUrl" . | quote }}
QUARKUS_DATASOURCE_JDBC_MAX_SIZE: {{ .Values.postgresql.connectionPool.maxSize | default "20" | quote }}
Expand Down
6 changes: 3 additions & 3 deletions charts/kates/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ spec:
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if or .Values.postgresql.enabled .Values.extraInitContainers }}
{{- if or .Values.postgresql.enabled .Values.cnpg.enabled .Values.extraInitContainers }}
initContainers:
{{- if .Values.postgresql.enabled }}
{{- if or .Values.postgresql.enabled .Values.cnpg.enabled }}
- name: wait-for-postgres
image: {{ include "kates.imageRef" (dict "image" .Values.initContainers.waitForPostgres.image "ctx" .) | quote }}
imagePullPolicy: {{ .Values.initContainers.waitForPostgres.pullPolicy | default "IfNotPresent" }}
command: ['sh', '-c', 'until nc -z {{ include "kates.postgresql.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.global.clusterDomain }} 5432; do echo "Waiting for PostgreSQL..."; sleep 2; done']
command: ['sh', '-c', 'until nc -z {{ include "kates.dbHost" . }} 5432; do echo "Waiting for PostgreSQL..."; sleep 2; done']
{{- with .Values.initContainers.waitForPostgres.securityContext | default .Values.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 10 }}
Expand Down
2 changes: 1 addition & 1 deletion charts/kates/templates/kyverno-policy-exceptions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,6 @@ spec:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: [{{ include "kates.name" $ | quote }}, {{ include "kates.postgresql.fullname" $ | quote }}]
values: [{{ include "kates.name" $ | quote }}, {{ include "kates.postgresql.fullname" $ | quote }}, {{ include "kates.cnpg.fullname" $ | quote }}]
{{- end }}
{{- end }}
4 changes: 2 additions & 2 deletions charts/kates/templates/migration-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ spec:
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.postgresql.enabled }}
{{- if or .Values.postgresql.enabled .Values.cnpg.enabled }}
initContainers:
- name: wait-for-postgres
image: {{ include "kates.imageRef" (dict "image" .Values.initContainers.waitForPostgres.image "ctx" .) | quote }}
imagePullPolicy: {{ .Values.initContainers.waitForPostgres.pullPolicy | default "IfNotPresent" }}
command: ['sh', '-c', 'until nc -z {{ include "kates.postgresql.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.global.clusterDomain }} 5432; do echo "Waiting for PostgreSQL..."; sleep 2; done']
command: ['sh', '-c', 'until nc -z {{ include "kates.dbHost" . }} 5432; do echo "Waiting for PostgreSQL..."; sleep 2; done']
{{- with .Values.initContainers.waitForPostgres.securityContext | default .Values.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 10 }}
Expand Down
9 changes: 9 additions & 0 deletions charts/kates/templates/networkpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ spec:
- port: 5432
protocol: TCP
{{- end }}
{{- if .Values.cnpg.enabled }}
- to:
- podSelector:
matchLabels:
cnpg.io/cluster: {{ include "kates.cnpg.fullname" . }}
ports:
- port: 5432
protocol: TCP
{{- end }}
{{- if .Values.externalDatabase.enabled }}
- to:
- ipBlock:
Expand Down
2 changes: 1 addition & 1 deletion charts/kates/templates/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
empty credentials. Enabled via productionMode (values-prod.yaml). */}}
{{- if .Values.productionMode }}
{{- if .Values.postgresql.enabled }}
{{- fail "productionMode: the embedded PostgreSQL StatefulSet is single-replica and intended for dev/test only. Use externalDatabase with existingSecret in production." }}
{{- fail "productionMode: the embedded PostgreSQL StatefulSet is single-replica and intended for dev/test only. Use cnpg.enabled, or externalDatabase with existingSecret, in production." }}
{{- end }}
{{- if and .Values.externalDatabase.enabled (not .Values.externalDatabase.existingSecret) (not .Values.externalDatabase.password) }}
{{- fail "productionMode: externalDatabase has no credentials. Set externalDatabase.existingSecret (recommended) or externalDatabase.password." }}
Expand Down
9 changes: 9 additions & 0 deletions charts/kates/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,15 @@
}
}
},
"cnpg": {
"type": "object",
"properties": {
"enabled": { "type": "boolean" },
"instances": { "type": "integer", "minimum": 1 },
"storage": { "type": "object" },
"resources": { "type": "object" }
}
},
"postgresql": {
"type": "object",
"properties": {
Expand Down
30 changes: 25 additions & 5 deletions charts/kates/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,8 @@ metrics:

# -- Bundled PostgreSQL configuration.
# A self-contained single-instance StatefulSet on the official `postgres`
# image (no Bitnami). For production HA, set enabled=false and point
# externalDatabase at a managed cluster (CloudNativePG recommended).
# image (no Bitnami). It does not recover alone from a node crash: for
# production HA, set enabled=false and use cnpg below.
postgresql:
# -- Deploy bundled PostgreSQL
enabled: true
Expand Down Expand Up @@ -640,10 +640,30 @@ postgresql:
# -- Minimum pool size
minSize: "5"

# -- Replicated PostgreSQL through CloudNativePG, which survives a node crash.
# Needs the operator installed first. Set postgresql.enabled=false with it; the
# database and owner names come from externalDatabase.database and .username,
# whose enabled stays false. No backups are configured: see the operator's docs.
cnpg:
# -- Render a CloudNativePG Cluster and connect the API to its primary
enabled: false
# -- PostgreSQL instances, each on its own node
instances: 3
storage:
# -- Volume size of each instance
size: 10Gi
# -- Storage class; the cluster's default when empty
storageClass: ""
# -- Requests and limits of each instance
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"

# -- External database configuration (when postgresql.enabled=false).
# Recommended for production HA: run a CloudNativePG `Cluster` (official
# postgres images, streaming replication, backups) and point host/port at its
# `-rw` service, e.g. host: kates-db-rw.database.svc — no Bitnami involved.
externalDatabase:
# -- Enable external database
enabled: false
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/mcp_tools_scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ var mcpCaveatsScenario = []mcpCaveat{
mcpAnchoredRef(mcpJava+"engine/TestOrchestrator.java:1045-1091", "TestSpec applyTypeDefaults(", "return merged;"),
mcpAnchoredRef("kates/src/main/resources/application.properties:73-114",
"# Per-type overrides: STRESS", "kates.tests.roundtrip.throughput=10000"),
mcpAnchoredRef("charts/kates/values.yaml:665-733", "defaults:", "tests:", `throughput: "10000"`),
mcpAnchoredRef("charts/kates/values.yaml:686-754", "defaults:", "tests:", `throughput: "10000"`),
mcpAnchoredRef("charts/kates/templates/configmap.yaml:88-184",
"KATES_TESTS_LOAD_REPLICATION_FACTOR:", "KATES_TESTS_ROUNDTRIP_NUM_CONSUMERS:"),
},
Expand Down
Loading
Loading