From 4e3291c4494b43add955cddd4703bc9969d4a921 Mon Sep 17 00:00:00 2001 From: Lars Francke Date: Thu, 10 Sep 2026 22:58:00 +0200 Subject: [PATCH] docs: Add values.schema.json and document the used values Follows hive-operator, but written for this chart: it is a CSI driver with a provisioner Deployment and a node driver DaemonSet, so the value surface is its own. Having a schema makes Helm validate values on install, upgrade, lint and template AND shows as a nice reference on ArtifactHub. One thing to note is that we include the "global" block even if it's not used by us. This is due to https://helm.sh/de/docs/chart_template_guide/subcharts_and_globals/ In short: When used as a subchart Helm injects a "global" into every subchart. If we don't declare it validation will fail when used as a subchart. The commented "priority" and "preemptionPolicy" examples are dropped: no template reads them, and with the schema in place uncommenting them would now fail instead of being ignored. This also documents all used values in values.yaml itself. NOTES.txt is not part of this since operator-templating#646 generates it. --- .../helm/listener-operator/values.schema.json | 573 ++++++++++++++++++ deploy/helm/listener-operator/values.yaml | 25 +- 2 files changed, 590 insertions(+), 8 deletions(-) create mode 100644 deploy/helm/listener-operator/values.schema.json diff --git a/deploy/helm/listener-operator/values.schema.json b/deploy/helm/listener-operator/values.schema.json new file mode 100644 index 00000000..c1cd9144 --- /dev/null +++ b/deploy/helm/listener-operator/values.schema.json @@ -0,0 +1,573 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "Stackable Listener Operator", + "description": "Values accepted by the operator Helm chart. The operator is a CSI driver and runs as two workloads: a provisioner Deployment and a node driver DaemonSet. How a workload is exposed is configured in Listener and ListenerClass custom resources, not here.", + "type": "object", + "additionalProperties": false, + "properties": { + "image": { + "title": "Operator image", + "type": "object", + "additionalProperties": false, + "properties": { + "repository": { + "title": "Operator image registry and namespace", + "description": "Registry and namespace holding the operator image, without the image name. Set automatically when the chart is packaged, from the registry it is published to: oci.stackable.tech/sdp or quay.io/stackable/sdp.", + "type": "string" + }, + "tag": { + "title": "Image tag", + "description": "Overrides the operator image tag. Defaults to the chart appVersion, which is the SDP release.", + "type": "string" + }, + "pullPolicy": { + "title": "Image pull policy", + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ], + "default": "IfNotPresent", + "description": "When to pull the operator image. Release tags are immutable, so IfNotPresent is enough. The CSI sidecars have their own pull policies." + }, + "pullSecrets": { + "title": "Image pull secrets", + "description": "Secrets used to pull the operator image from a private registry. These apply to the listener-operator pods only, not to the pods that mount its volumes.", + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "description": "Where the operator image is pulled from and how. Used by both the provisioner Deployment and the node driver DaemonSet." + }, + "csiProvisioner": { + "title": "CSI provisioner", + "type": "object", + "additionalProperties": false, + "properties": { + "podAnnotations": { + "title": "Pod annotations", + "description": "Annotations added to the provisioner pod.", + "type": "object", + "default": {} + }, + "podSecurityContext": { + "title": "Pod security context", + "description": "Pod-level security context for the provisioner pod, for example fsGroup. Passed through to Kubernetes unchanged.", + "type": "object", + "default": {} + }, + "nodeSelector": { + "title": "Node selector", + "description": "Node labels the provisioner pod must match to be scheduled.", + "type": "object", + "default": {} + }, + "tolerations": { + "title": "Tolerations", + "description": "Taints the provisioner pod tolerates.", + "type": "array", + "default": [] + }, + "affinity": { + "title": "Affinity", + "description": "Affinity and anti-affinity rules for the provisioner pod.", + "type": "object", + "default": {} + }, + "priorityClassName": { + "title": "Priority class name", + "description": "PriorityClass for the provisioner pod. Unset means the cluster default.", + "type": "string" + }, + "controllerService": { + "title": "Controller container", + "type": "object", + "additionalProperties": false, + "properties": { + "resources": { + "title": "Resource requests and limits", + "description": "CPU and memory for the operator's controller container.", + "allOf": [ + { + "$ref": "#/definitions/resources" + } + ] + }, + "securityContext": { + "title": "Container security context", + "description": "Container-level security context for the controller container. It runs as root and, on SELinux systems, as a Super Privileged Container, because it writes into the Listener volumes.", + "type": "object" + } + }, + "description": "The operator's own container in the provisioner Deployment." + }, + "externalProvisioner": { + "title": "external-provisioner sidecar", + "type": "object", + "additionalProperties": false, + "properties": { + "image": { + "title": "external-provisioner image", + "type": "object", + "additionalProperties": false, + "properties": { + "repository": { + "title": "Registry and namespace", + "description": "Registry and namespace holding the sidecar image, without the image name. Defaults to image.repository with /sig-storage appended, so the csi-provisioner sidecar is pulled from the same registry as the operator.", + "type": "string" + }, + "tag": { + "title": "Image tag", + "description": "Version of csi-provisioner to run. Pinned by the chart; change it only to match a mirror or to work around a specific upstream bug.", + "type": "string", + "default": "v5.3.0" + }, + "pullPolicy": { + "title": "Image pull policy", + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ], + "default": "IfNotPresent", + "description": "When to pull the sidecar image." + } + }, + "description": "Where the csi-provisioner sidecar image is pulled from and how." + }, + "resources": { + "title": "Resource requests and limits", + "description": "CPU and memory for the csi-provisioner sidecar.", + "allOf": [ + { + "$ref": "#/definitions/resources" + } + ] + } + }, + "description": "The upstream csi-provisioner sidecar, which turns PersistentVolumeClaims into CSI provisioning calls." + } + }, + "description": "The provisioner Deployment. One replica cluster-wide, it reconciles Listener objects and the Services behind them." + }, + "csiNodeDriver": { + "title": "CSI node driver", + "type": "object", + "additionalProperties": false, + "properties": { + "podAnnotations": { + "title": "Pod annotations", + "description": "Annotations added to the node driver pods.", + "type": "object", + "default": {} + }, + "podSecurityContext": { + "title": "Pod security context", + "description": "Pod-level security context for the node driver pods, for example fsGroup. Passed through to Kubernetes unchanged.", + "type": "object", + "default": {} + }, + "nodeSelector": { + "title": "Node selector", + "description": "Node labels the node driver pods must match to be scheduled.", + "type": "object", + "default": {} + }, + "tolerations": { + "title": "Tolerations", + "description": "Taints the node driver pods tolerates.", + "type": "array", + "default": [] + }, + "affinity": { + "title": "Affinity", + "description": "Affinity and anti-affinity rules for the node driver pods.", + "type": "object", + "default": {} + }, + "priorityClassName": { + "title": "Priority class name", + "description": "PriorityClass for the node driver pods. Unset means the cluster default.", + "type": "string" + }, + "nodeService": { + "title": "Node driver container", + "type": "object", + "additionalProperties": false, + "properties": { + "resources": { + "title": "Resource requests and limits", + "description": "CPU and memory for the operator's node driver container.", + "allOf": [ + { + "$ref": "#/definitions/resources" + } + ] + }, + "securityContext": { + "title": "Container security context", + "description": "Container-level security context for the node driver container. It runs as root and, on SELinux systems, as a Super Privileged Container, because it writes into the Listener volumes.", + "type": "object" + } + }, + "description": "The operator's own container in the node driver DaemonSet." + }, + "nodeDriverRegistrar": { + "title": "node-driver-registrar sidecar", + "type": "object", + "additionalProperties": false, + "properties": { + "image": { + "title": "node-driver-registrar image", + "type": "object", + "additionalProperties": false, + "properties": { + "repository": { + "title": "Registry and namespace", + "description": "Registry and namespace holding the sidecar image, without the image name. Defaults to image.repository with /sig-storage appended, so the csi-node-driver-registrar sidecar is pulled from the same registry as the operator.", + "type": "string" + }, + "tag": { + "title": "Image tag", + "description": "Version of csi-node-driver-registrar to run. Pinned by the chart; change it only to match a mirror or to work around a specific upstream bug.", + "type": "string", + "default": "v2.15.0" + }, + "pullPolicy": { + "title": "Image pull policy", + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ], + "default": "IfNotPresent", + "description": "When to pull the sidecar image." + } + }, + "description": "Where the csi-node-driver-registrar sidecar image is pulled from and how." + }, + "resources": { + "title": "Resource requests and limits", + "description": "CPU and memory for the csi-node-driver-registrar sidecar.", + "allOf": [ + { + "$ref": "#/definitions/resources" + } + ] + } + }, + "description": "The upstream csi-node-driver-registrar sidecar, which registers the driver with the kubelet on each node." + } + }, + "description": "The node driver DaemonSet. One pod per node, it mounts Listener volumes into the pods scheduled there." + }, + "nameOverride": { + "title": "Name override", + "description": "Replaces the chart name inside generated resource names. The release name is still prefixed. Installing release my-release with nameOverride=foo gives my-release-foo-csi-provisioner instead of my-release-listener-operator-csi-provisioner.", + "type": "string", + "default": "" + }, + "fullnameOverride": { + "title": "Full name override", + "description": "Replaces the generated name entirely, including the release name prefix. Installing release my-release with fullnameOverride=bar gives bar-csi-provisioner instead of my-release-listener-operator-csi-provisioner.", + "type": "string", + "default": "" + }, + "serviceAccount": { + "title": "Service account", + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "title": "Create the service account", + "type": "boolean", + "default": true, + "description": "Whether the chart creates the ServiceAccount the operator runs as. Set to false to bring your own, then name it in serviceAccount.name. The ClusterRole and the ClusterRoleBinding that grants it are created by the chart either way, so an existing ServiceAccount still receives the operator's permissions." + }, + "annotations": { + "title": "Service account annotations", + "type": "object", + "default": {}, + "description": "Annotations on the created ServiceAccount, for example an IAM role for IRSA or Workload Identity. Only applies when serviceAccount.create is true." + }, + "name": { + "title": "Service account name", + "description": "Name of the ServiceAccount the operator pods run as. Leave empty to use the generated name, -listener-operator-serviceaccount. Required when serviceAccount.create is false, where it tells the chart which existing ServiceAccount the workloads should run as and the ClusterRoleBinding should grant.", + "type": "string", + "default": "" + } + }, + "description": "Identity the operator pods run as. The chart binds it to the operator's ClusterRole, which is what grants permission to watch and manage its custom resources cluster-wide. Both the controller and the node driver use it." + }, + "labels": { + "title": "Labels", + "description": "Labels attached to every resource the chart deploys.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": { + "stackable.tech/vendor": "Stackable" + } + }, + "kubernetesClusterDomain": { + "title": "Kubernetes cluster domain", + "description": "Set this when the cluster does not use the default cluster.local domain. See https://docs.stackable.tech/home/stable/guides/kubernetes-cluster-domain", + "type": "string" + }, + "kubeletDir": { + "title": "Kubelet directory", + "description": "Root directory of the kubelet on each node, where the node driver registers its socket and finds the pod volume directories. The Kubernetes default is /var/lib/kubelet; distributions such as microk8s and k3s use a different path. See https://github.com/stackabletech/secret-operator/issues/229", + "type": "string", + "default": "/var/lib/kubelet" + }, + "preset": { + "title": "Preinstalled ListenerClasses", + "description": "Which set of ListenerClasses the operator creates on startup. ephemeral-nodes suits cloud environments with short-lived nodes and needs a LoadBalancer controller. stable-nodes suits on-premise clusters with long-lived nodes and no LoadBalancer controller. none installs nothing, leaving the ListenerClasses to the administrator.", + "type": "string", + "enum": [ + "none", + "stable-nodes", + "ephemeral-nodes" + ], + "default": "ephemeral-nodes" + }, + "maintenance": { + "title": "Maintenance behaviour", + "type": "object", + "additionalProperties": false, + "properties": { + "endOfSupportCheck": { + "title": "End-of-support check", + "description": "Warns when the running SDP release has reached end of support.", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "description": "Whether the operator logs a warning once the running SDP release is out of support." + }, + "mode": { + "title": "Check mode", + "description": "Only offline is implemented: the check uses the release date compiled into the operator and makes no network calls.", + "type": "string", + "enum": [ + "offline" + ] + }, + "interval": { + "title": "Check interval", + "description": "How often the check runs, as a duration such as 24h.", + "type": "string" + } + } + }, + "customResourceDefinitions": { + "title": "CustomResourceDefinitions", + "type": "object", + "additionalProperties": false, + "properties": { + "maintain": { + "title": "Let the operator manage its CRDs", + "description": "The operator applies and updates its own CRDs at startup. Disable this only if CRDs are applied out of band, for example by a cluster admin with elevated rights.", + "type": "boolean", + "default": true + } + }, + "description": "How the operator handles its own CustomResourceDefinitions." + } + }, + "description": "Background housekeeping the operator performs, independent of reconciling custom resources." + }, + "telemetry": { + "title": "Telemetry", + "description": "Operator logging and tracing. See https://docs.stackable.tech/home/stable/concepts/telemetry/", + "type": "object", + "additionalProperties": false, + "properties": { + "consoleLog": { + "title": "Console logs", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "description": "Whether the operator logs to stdout." + }, + "level": { + "title": "Console log level", + "description": "Verbosity of the console logs. A tracing filter directive, for example INFO, or something more targeted like info,stackable_listener_operator=debug.", + "allOf": [ + { + "$ref": "#/definitions/logLevel" + } + ] + }, + "format": { + "title": "Log format", + "type": "string", + "enum": [ + "plain", + "json" + ], + "default": "plain", + "description": "plain is human-readable and coloured unless NO_COLOR is set. json is structured, for log collectors that parse it." + } + }, + "description": "Logs written to stdout, which is what kubectl logs shows." + }, + "fileLog": { + "title": "File logs", + "description": "Writes logs to /stackable/logs inside the container.", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Whether the operator also writes logs to /stackable/logs inside the container. Off by default: it needs a volume to be useful, otherwise the logs vanish with the pod." + }, + "level": { + "title": "File log level", + "description": "Verbosity of the file logs, independent of the console log level. Same syntax.", + "allOf": [ + { + "$ref": "#/definitions/logLevel" + } + ] + }, + "rotationPeriod": { + "title": "Rotation period", + "type": "string", + "enum": [ + "minutely", + "hourly", + "daily", + "never" + ], + "default": "hourly", + "description": "How often a new log file is started." + }, + "maxFiles": { + "title": "Files to keep", + "type": "integer", + "minimum": 1, + "default": 6, + "description": "How many rotated files to keep. Older ones are deleted." + } + } + }, + "otelLogExporter": { + "title": "OpenTelemetry log exporter", + "description": "Exports operator logs to an OTLP collector.", + "allOf": [ + { + "$ref": "#/definitions/otelExporter" + } + ] + }, + "otelTraceExporter": { + "title": "OpenTelemetry trace exporter", + "description": "Exports operator traces to an OTLP collector.", + "allOf": [ + { + "$ref": "#/definitions/otelExporter" + } + ] + } + } + }, + "global": { + "title": "Global values", + "description": "Values shared with parent and sibling charts when this chart is used as a subchart. Not read by this chart.", + "type": "object" + } + }, + "definitions": { + "resourceQuantities": { + "type": "object", + "additionalProperties": false, + "properties": { + "cpu": { + "title": "CPU", + "description": "Kubernetes quantity, for example 100m or 1.", + "type": [ + "string", + "number" + ] + }, + "memory": { + "title": "Memory", + "description": "Kubernetes quantity, for example 128Mi.", + "type": [ + "string", + "number" + ] + } + } + }, + "logLevel": { + "title": "Log level", + "description": "A tracing filter directive, for example INFO, or something more specific like info,stackable_listener_operator=debug. Not a fixed set of values.", + "type": "string" + }, + "otelExporter": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "level": { + "$ref": "#/definitions/logLevel" + }, + "endpoint": { + "title": "OTLP endpoint", + "description": "Collector to send to. Defaults to the OpenTelemetry SDK default when unset.", + "type": "string" + } + } + }, + "resources": { + "type": "object", + "additionalProperties": false, + "properties": { + "limits": { + "title": "Limits", + "description": "Maximum CPU and memory the container may use.", + "allOf": [ + { + "$ref": "#/definitions/resourceQuantities" + } + ] + }, + "requests": { + "title": "Requests", + "description": "CPU and memory reserved for the container.", + "allOf": [ + { + "$ref": "#/definitions/resourceQuantities" + } + ] + } + } + } + } +} diff --git a/deploy/helm/listener-operator/values.yaml b/deploy/helm/listener-operator/values.yaml index 7031ea21..e809f894 100644 --- a/deploy/helm/listener-operator/values.yaml +++ b/deploy/helm/listener-operator/values.yaml @@ -2,9 +2,14 @@ --- # Used by both the Controller Service and Node Service containers image: - # By default, the correct registry is automatically selected based on the Helm Chart installation source + # Registry and namespace holding the operator image, without the image name. + # Set when the chart is packaged, from the registry it is published to, so it is + # normally already correct. Override it to pull from a mirror. # repository: oci.stackable.tech/sdp + + # Overrides the operator image tag, which defaults to the chart appVersion. # tag: 0.0.0-dev + pullPolicy: IfNotPresent pullSecrets: [] @@ -20,9 +25,8 @@ csiProvisioner: affinity: {} - # priority: ... - # priorityClassName: ... - # preemptionPolicy: ... + # PriorityClass for the provisioner pod. Unset means the cluster default. + # priorityClassName: system-cluster-critical controllerService: resources: @@ -50,7 +54,8 @@ csiProvisioner: externalProvisioner: image: - # By default, the correct registry is automatically selected based on the Helm Chart installation source + # Registry and namespace holding the sidecar image. Defaults to image.repository + # with /sig-storage appended. Override it to pull from a mirror. # repository: oci.stackable.tech/sdp/sig-storage tag: v5.3.0 pullPolicy: IfNotPresent @@ -74,7 +79,8 @@ csiNodeDriver: affinity: {} - # priorityClassName: ... + # PriorityClass for the node driver pods. Unset means the cluster default. + # priorityClassName: system-node-critical nodeService: resources: @@ -102,7 +108,8 @@ csiNodeDriver: nodeDriverRegistrar: image: - # By default, the correct registry is automatically selected based on the Helm Chart installation source + # Registry and namespace holding the sidecar image. Defaults to image.repository + # with /sig-storage appended. Override it to pull from a mirror. # repository: oci.stackable.tech/sdp/sig-storage tag: v2.15.0 pullPolicy: IfNotPresent @@ -123,7 +130,9 @@ serviceAccount: # Annotations to add to the service account annotations: {} # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template + # Leave empty to use the generated name, -listener-operator-serviceaccount. + # Required when create is false, so that the workloads and the ClusterRoleBinding + # know which existing service account to use. name: "" # Provide additional labels which get attached to all deployed resources