refactor(kafka): drop null from tls.enabled schema, use kindIs invalid pattern

cozyvalues-gen v1.5.0 natively emits *bool for @param {*bool} fields,
so the post-generate sed patch for types.go and the jq patch forcing
["boolean","null"] on values.schema.json are no longer needed. Remove
both patches from the Makefile.

Regenerate values.schema.json and cozyrds/kafka.yaml so tls.enabled
carries "type":"boolean" instead of ["boolean","null"]. The single-string
type is required by apiextv1.JSONSchemaProps, which cozystack-api uses for
server-side defaulting; the array form silently disabled defaulting for
the Kafka app.

Refactor the inline tri-state TLS guard in kafka.yaml to use the canonical
kindIs "invalid" pattern and extract it into a dedicated _tls.tpl named
template (kafka.tls.enabled). Add three explicit test cases (J-1, J-2, J-3)
covering the inherit-from-external path, the explicit-false override, and
the external-gate suppression cases.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
This commit is contained in:
Arsolitt committed 2026-05-27 11:16:42 +03:00
1 parent 19634fd45d
commit 244f19afa3
6 files changed
+76 -20

No files matched your search

-4
View File
@@ -3,10 +3,6 @@ PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
generate:
cozyvalues-gen -m 'kafka' -v values.yaml -s values.schema.json -r README.md -g ../../../api/apps/v1alpha1/kafka/types.go
sed -i 's/Enabled bool `json:"enabled,omitempty"`/Enabled *bool `json:"enabled,omitempty"`/' ../../../api/apps/v1alpha1/kafka/types.go
grep -q 'Enabled \*bool `json:"enabled,omitempty"`' ../../../api/apps/v1alpha1/kafka/types.go || (echo "ERROR: failed to patch TLS.Enabled to *bool in types.go" && exit 1)
jq '.properties.tls.properties.enabled.type = ["boolean", "null"]' values.schema.json > values.schema.json.tmp && mv values.schema.json.tmp values.schema.json
jq -e '.properties.tls.properties.enabled.type == ["boolean", "null"]' values.schema.json > /dev/null || (echo "ERROR: jq patch failed" && exit 1)
../../../hack/update-crd.sh
test:
+20
View File
@@ -0,0 +1,20 @@
{{- /*
kafka.tls.enabled — tri-state TLS decision for the external listener (port 9094).
Resolution order:
1. If tls.enabled is explicitly set (true or false), that value wins.
2. If tls.enabled is absent (nil / not provided), TLS inherits from .Values.external.
Null detection uses `kindIs "invalid"` which is the canonical Helm pattern for
distinguishing an absent/nil value from an explicit false.
Note: this helper only returns the TLS flag value. Whether the external listener
is rendered at all is controlled by .Values.external in the caller.
*/ -}}
{{- define "kafka.tls.enabled" -}}
{{- if kindIs "invalid" .Values.tls.enabled -}}
{{- .Values.external | default false -}}
{{- else -}}
{{- .Values.tls.enabled -}}
{{- end -}}
{{- end -}}
+3 -10
View File
@@ -5,21 +5,14 @@
- tls.enabled controls TLS *on the external listener* (independent of whether
the listener exists):
- If tls.enabled is explicitly set (true or false), that value wins.
- If tls.enabled is unset (nil), TLS inherits from .Values.external
- If tls.enabled is unset (nil / absent), TLS inherits from .Values.external
(auto-on when external is true, off otherwise).
- tls.enabled does NOT control whether the external listener is created.
Setting tls.enabled=true with external=false has no effect on listeners.
Internal TLS listener (port 9093) is always on; Strimzi manages the PKI.
Null detection uses `kindIs "invalid"` (canonical pattern) — see _tls.tpl.
*/ -}}
{{- $tlsMap := .Values.tls | default dict -}}
{{- $tlsEnabledRaw := index $tlsMap "enabled" -}}
{{- $tlsEnabledExplicit := and (hasKey $tlsMap "enabled") (ne $tlsEnabledRaw nil) -}}
{{- $tlsEnabled := false -}}
{{- if $tlsEnabledExplicit -}}
{{- $tlsEnabled = $tlsEnabledRaw -}}
{{- else -}}
{{- $tlsEnabled = .Values.external | default false -}}
{{- end -}}
{{- $tlsEnabled := include "kafka.tls.enabled" . | trim -}}
{{- $showExternal := .Values.external -}}
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
@@ -97,6 +97,56 @@ tests:
path: spec.kafka.listeners
count: 2
########################################
# kindIs "invalid" guard — Category J #
########################################
# J-1: external=true, tls.enabled absent → inherit path: TLS is on
- it: "kindIs-guard: TLS inherits from external when tls.enabled is absent and external is true"
release:
name: test-kafka
namespace: tenant-test
set:
external: true
asserts:
- contains:
path: spec.kafka.listeners
content:
name: external
port: 9094
type: loadbalancer
tls: true
# J-2: external=true, tls.enabled=false → explicit override wins: TLS is off
- it: "kindIs-guard: explicit tls.enabled=false overrides external=true"
release:
name: test-kafka
namespace: tenant-test
set:
external: true
tls.enabled: false
asserts:
- contains:
path: spec.kafka.listeners
content:
name: external
port: 9094
type: loadbalancer
tls: false
# J-3: external=false, tls.enabled=true → external gate wins: no listener rendered
- it: "kindIs-guard: external gate suppresses listener even when tls.enabled=true"
release:
name: test-kafka
namespace: tenant-test
set:
external: false
tls.enabled: true
asserts:
- lengthEqual:
path: spec.kafka.listeners
count: 2
########################################
# Internal listeners unaffected by TLS #
########################################
+2 -5
View File
@@ -8,16 +8,13 @@
"default": false
},
"tls": {
"description": "TLS configuration. Strimzi manages the cluster PKI automatically (no cert-manager is involved for this chart): the operator auto-creates `<release>-cluster-ca-cert` and `<release>-clients-ca-cert` secrets, both exposed for client trust setup. The internal TLS listener on 9093 is always on; this toggle only controls the external listener on 9094.",
"description": "TLS configuration. Strimzi manages the cluster PKI automatically (no cert-manager is involved for this chart): the operator auto-creates `\u003crelease\u003e-cluster-ca-cert` and `\u003crelease\u003e-clients-ca-cert` secrets, both exposed for client trust setup. The internal TLS listener on 9093 is always on; this toggle only controls the external listener on 9094.",
"type": "object",
"default": {},
"properties": {
"enabled": {
"description": "Enable TLS on the external listener. When unset, inherits the value of `external` (TLS is on when external access is enabled). Warning: setting this to false while external is true exposes Kafka over plaintext on a public IP via LoadBalancer. Strimzi does not provide authentication on this listener unless SCRAM, mTLS, or OAuth is separately configured. Use only in controlled networks.",
"type": [
"boolean",
"null"
]
"type": "boolean"
}
}
},
+1 -1
View File
@@ -8,7 +8,7 @@ spec:
plural: kafkas
singular: kafka
openAPISchema: |-
{"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"tls":{"description":"TLS configuration. Strimzi manages the cluster PKI automatically (no cert-manager is involved for this chart): the operator auto-creates `<release>-cluster-ca-cert` and `<release>-clients-ca-cert` secrets, both exposed for client trust setup. The internal TLS listener on 9093 is always on; this toggle only controls the external listener on 9094.","type":"object","default":{},"properties":{"enabled":{"description":"Enable TLS on the external listener. When unset, inherits the value of `external` (TLS is on when external access is enabled). Warning: setting this to false while external is true exposes Kafka over plaintext on a public IP via LoadBalancer. Strimzi does not provide authentication on this listener unless SCRAM, mTLS, or OAuth is separately configured. Use only in controlled networks.","type":["boolean","null"]}}},"topics":{"description":"Topics configuration.","type":"array","default":[],"items":{"type":"object","required":["config","name","partitions","replicas"],"properties":{"config":{"description":"Topic configuration.","type":"object","x-kubernetes-preserve-unknown-fields":true},"name":{"description":"Topic name.","type":"string"},"partitions":{"description":"Number of partitions.","type":"integer"},"replicas":{"description":"Number of replicas.","type":"integer"}}}},"kafka":{"description":"Kafka configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of Kafka replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"c1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size for Kafka.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the Kafka data.","type":"string","default":"","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}]}}},"zookeeper":{"description":"ZooKeeper configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of ZooKeeper replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"c1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlaLine truncated
{"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"tls":{"description":"TLS configuration. Strimzi manages the cluster PKI automatically (no cert-manager is involved for this chart): the operator auto-creates `<release>-cluster-ca-cert` and `<release>-clients-ca-cert` secrets, both exposed for client trust setup. The internal TLS listener on 9093 is always on; this toggle only controls the external listener on 9094.","type":"object","default":{},"properties":{"enabled":{"description":"Enable TLS on the external listener. When unset, inherits the value of `external` (TLS is on when external access is enabled). Warning: setting this to false while external is true exposes Kafka over plaintext on a public IP via LoadBalancer. Strimzi does not provide authentication on this listener unless SCRAM, mTLS, or OAuth is separately configured. Use only in controlled networks.","type":"boolean"}}},"topics":{"description":"Topics configuration.","type":"array","default":[],"items":{"type":"object","required":["config","name","partitions","replicas"],"properties":{"config":{"description":"Topic configuration.","type":"object","x-kubernetes-preserve-unknown-fields":true},"name":{"description":"Topic name.","type":"string"},"partitions":{"description":"Number of partitions.","type":"integer"},"replicas":{"description":"Number of replicas.","type":"integer"}}}},"kafka":{"description":"Kafka configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of Kafka replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"c1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size for Kafka.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the Kafka data.","type":"string","default":"","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}]}}},"zookeeper":{"description":"ZooKeeper configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of ZooKeeper replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"c1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.Line truncated
release:
prefix: kafka-
labels: