{{- $nifiHome := "/opt/nifi/nifi-current" }} {{- /* Validate that basic auth is only used with single node */ -}} {{- if and (not .Values.global.oidc.enabled) (not .Values.global.ldap.enabled) (gt (.Values.global.nifi.nodeCount | int) 1) }} {{- fail "Basic authentication (Single User Authentication) only supports single-node deployment. Please set global.nifi.nodeCount to 1, or enable OIDC/LDAP authentication for clustering." }} {{- end }} apiVersion: apps/v1 kind: StatefulSet metadata: name: {{ include "nifi.fullname" . }} labels: {{- include "nifi.labels" . | nindent 4 }} spec: podManagementPolicy: Parallel replicas: {{ .Values.global.nifi.nodeCount }} selector: matchLabels: {{- include "nifi.selectorLabels" . | nindent 6 }} serviceName: {{ include "nifi.fullname" . }} template: metadata: labels: {{- include "nifi.selectorLabels" . | nindent 8 }} spec: terminationGracePeriodSeconds: {{ .Values.shutdown.podTerminationGracePeriodSeconds }} {{- if or .Values.persistence.takeOwnershipOnStartup .Values.extraTakeOwnershipPaths }} initContainers: {{- if eq (include "nifi.useZooKeeper" .) "true" }} - name: wait-for-zookeeper image: {{ .Values.global.busybox.repository }} command: - sh - -c - | {{- if .Values.zookeeper.enabled }} echo "Connecting to embedded ZooKeeper {{ .Release.Name }}-zookeeper" until nc -vzw 1 {{ .Release.Name }}-zookeeper {{ .Values.zookeeper.external.port | default 2181 }} ; do echo "Waiting for zookeeper to start" sleep 3 done {{- else }} echo "Connecting to external ZooKeeper {{ .Values.zookeeper.external.url }}" {{- if .Values.zookeeper.external.url }} {{- range $url := splitList "," .Values.zookeeper.external.url }} {{- $hostPort := splitList ":" $url }} {{- if gt (len $hostPort) 1 }} until nc -vzw 1 {{ index $hostPort 0 }} {{ index $hostPort 1 }} ; do echo "Waiting for zookeeper {{ $url }} to start" sleep 3 done {{- else }} until nc -vzw 1 {{ index $hostPort 0 }} 2181 ; do echo "Waiting for zookeeper {{ $url }}:2181 to start" sleep 3 done {{- end }} {{- end }} {{- else }} echo "No external ZooKeeper URL configured" {{- end }} {{- end }} resources: requests: cpu: 20m memory: 10Mi limits: cpu: 20m memory: 10Mi {{- else }} - name: check-kubernetes-permissions image: {{ .Values.global.busybox.repository }} command: - sh - -c - | echo "Using Kubernetes state management for NiFi {{ .Chart.AppVersion }}" echo "Checking Kubernetes API permissions..." echo "Namespace: {{ include "nifi.stateManagementNamespace" . }}" echo "Lease prefix: {{ .Values.stateManagement.kubernetes.leasePrefix }}" echo "State prefix: {{ .Values.stateManagement.kubernetes.statePrefix }}" echo "✅ Kubernetes state management configured" resources: requests: cpu: 20m memory: 10Mi limits: cpu: 20m memory: 10Mi {{- end }} securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true capabilities: drop: - ALL - name: take-ownership image: {{ .Values.global.busybox.repository }} command: - sh - -c - | {{- if .Values.persistence.takeOwnershipOnStartup }} chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/persistent_conf chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/state chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/logs chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/flowfile_repo chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/content_repo chown {{ .Values.securityContext.runAsUser | int64 }}:{{ .Values.securityContext.runAsGroup | int64 }} {{ $nifiHome }}/provenance_repo {{- end }} {{- range .Values.extraTakeOwnershipPaths }} chown {{ $.Values.securityContext.runAsUser | int64 }}:{{ $.Values.securityContext.runAsGroup | int64 }} {{ . | quote }} {{- end }} securityContext: runAsUser: 0 runAsNonRoot: false volumeMounts: - name: config mountPath: {{ $nifiHome }}/persistent_conf - name: state mountPath: {{ $nifiHome }}/state - name: logs mountPath: {{ $nifiHome }}/logs {{- if .Values.persistence.takeOwnershipOnStartup }} - name: flowfile mountPath: {{ $nifiHome }}/flowfile_repo - name: content mountPath: {{ $nifiHome }}/content_repo - name: provenance mountPath: {{ $nifiHome }}/provenance_repo {{- end }} {{- with .Values.extraVolumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} {{- end }} containers: - name: {{ .Chart.Name }} image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} imagePullPolicy: {{ .Values.image.pullPolicy }} command: [ "../scripts/k8s/custom-startup.sh" ] lifecycle: preStop: exec: command: [ "../scripts/k8s/pre-stop.sh" ] env: - name: NIFI_SENSITIVE_PROPS_KEY valueFrom: secretKeyRef: name: {{ default "encryption-sensitive-key" .Values.global.encryption.sensitivePropertiesKey.secretRef.name | quote }} key: {{ default "sensitivekey" .Values.global.encryption.sensitivePropertiesKey.secretRef.key | quote }} - name: NIFI_JVM_HEAP_INIT value: {{ .Values.jvmHeap.min | quote }} - name: NIFI_JVM_HEAP_MAX value: {{ .Values.jvmHeap.max | quote }} - name: NIFI_WEB_HTTPS_PORT value: {{ .Values.ports.https | quote }} - name: NIFI_REMOTE_INPUT_SOCKET_PORT value: {{ .Values.ports.remoteinput | quote }} - name: NIFI_CLUSTER_NODE_PROTOCOL_PORT value: {{ .Values.ports.cluster | quote }} - name: NIFI_CLUSTER_IS_NODE value: "true" - name: NIFI_ELECTION_MAX_CANDIDATES value: {{ .Values.global.nifi.nodeCount | quote }} {{- /* Common cluster environment variables (required for both state management strategies) */}} {{- include "nifi.clusterEnvironment" . | nindent 12 }} {{- if eq (include "nifi.useZooKeeper" .) "true" }} {{- include "nifi.zookeeperStateEnvironment" . | nindent 12 }} {{- else }} {{- include "nifi.kubernetesStateEnvironment" . | nindent 12 }} {{- end }} {{- include "nifi.keystoreEnvironment" . | nindent 12 }} {{- /* Authentication environment variables with priority: OIDC > LDAP > Basic Auth */}} {{- if .Values.global.oidc.enabled }} {{- /* OIDC has highest priority */}} {{- include "nifi.oidcEnvironment" . | nindent 12 }} {{- else if .Values.global.ldap.enabled }} {{- /* LDAP is second priority (only if OIDC is disabled) */}} {{- include "nifi.ldapEnvironment" . | nindent 12 }} {{- else }} {{- /* Basic auth is the default fallback (when both OIDC and LDAP are disabled) */}} {{- include "nifi.basicAuthEnvironment" . | nindent 12 }} {{- end }} {{- with .Values.extraEnv }} {{ toYaml . | nindent 12 }} {{- end }} ports: {{- range $name, $number := .Values.ports }} - name: {{ $name }} containerPort: {{ $number }} protocol: TCP {{- end }} {{- if .Values.extraPorts }} {{- range $name, $port := .Values.extraPorts }} - name: {{ $name }} containerPort: {{ $port.containerPort }} protocol: {{ $port.protocol | default "TCP" }} {{- end }} {{- end }} volumeMounts: - mountPath: {{ include "nifi.certPath" . }} name: certs readOnly: true - mountPath: {{ include "nifi.tlsPath" . }} name: tls - mountPath: /opt/nifi/scripts/k8s name: config-script readOnly: true - name: config mountPath: {{ $nifiHome }}/persistent_conf - name: state mountPath: {{ $nifiHome }}/state - name: logs mountPath: {{ $nifiHome }}/logs - name: flowfile mountPath: {{ $nifiHome }}/flowfile_repo - name: content mountPath: {{ $nifiHome }}/content_repo - name: provenance mountPath: {{ $nifiHome }}/provenance_repo {{- with .Values.extraVolumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- if not .Values.debugStartup }} startupProbe: tcpSocket: port: https {{- toYaml .Values.probeTimings.startup | nindent 12 }} readinessProbe: exec: command: - sh - -c - curl -k -s https://${HOSTNAME}.{{ include "nifi.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.ports.https }}/nifi {{- toYaml .Values.probeTimings.readiness | nindent 12 }} livenessProbe: exec: command: - sh - -c - curl -k -s https://${HOSTNAME}.{{ include "nifi.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.ports.https }}/nifi {{- toYaml .Values.probeTimings.liveness | nindent 12 }} {{- end }} {{- if .Values.filebeat.enabled }} - name: filebeat image: {{ .Values.filebeat.image.repository }}:{{ .Values.filebeat.image.tag | default "latest" }} args: - -e - -E - http.enabled=true volumeMounts: - mountPath: /usr/share/filebeat/filebeat.yml subPath: filebeat.yml name: filebeat-config readOnly: true - mountPath: /nifi/logs {{- with .Values.persistence.logs.volumeMount }} name: {{ .name | default "logs" }} {{- if .subPath }} subPath: {{ .subPath }} {{- end }} {{- end }} {{- with .Values.filebeat.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} {{- with .Values.filebeat.securityContext }} securityContext: {{- toYaml . | nindent 12 }} {{- end }} startupProbe: exec: command: - sh - -c - | #!/usr/bin/env bash -e filebeat test input {{- toYaml .Values.probeTimings.startup | nindent 12 }} readinessProbe: exec: command: - sh - -c - | #!/usr/bin/env bash -e filebeat test output {{- toYaml .Values.probeTimings.readiness | nindent 12 }} livenessProbe: exec: command: - sh - -c - | #!/usr/bin/env bash -e curl --fail 127.0.0.1:5066 {{- toYaml .Values.probeTimings.liveness | nindent 12 }} {{- end }} volumes: - name: certs secret: secretName: {{ include "nifi.fullname" . }}-tls # The secret created by cert-manager optional: false # Secret must be present - name: tls emptyDir: { } - name: config-script configMap: name: {{ include "nifi.fullname" . }} defaultMode: 0554 {{- with .Values.persistence.logs.volumeMount }} {{- /* Use an emptyDir volume if no persistence is configured */}} {{- if not .name }} - name: logs emptyDir: { } {{- end }} {{- end }} {{- with .Values.extraVolumes }} {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.filebeat }} {{- if .enabled }} - name: filebeat-config configMap: name: {{ include "nifi.fullname" $ }}-filebeat {{- end }} {{- end }} {{- with .Values.global.serviceAccount }} serviceAccountName: {{ .name | default (include "nifi.fullname" $) }} {{- end }} securityContext: {{- toYaml .Values.securityContext | nindent 8 }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} affinity: {{- if .Values.affinity }} {{- toYaml . | nindent 8 }} {{- else }} podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchLabels: {{- include "nifi.selectorLabels" . | nindent 20 }} topologyKey: kubernetes.io/hostname weight: 100 {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.topologySpreadConstraints }} topologySpreadConstraints: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.volumeClaims }} volumeClaimTemplates: {{- range $name, $volumeClaim := . }} - metadata: name: {{ $name }} spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: {{ $volumeClaim.size }} storageClassName: {{ $volumeClaim.storageClass }} {{- end }} {{- end }} --- apiVersion: v1 kind: Service metadata: name: {{ include "nifi.fullname" . }} labels: {{- include "nifi.labels" . | nindent 4 }} spec: type: ClusterIP clusterIP: None publishNotReadyAddresses: true ports: {{- range $name, $number := .Values.ports }} - name: {{ $name }} port: {{ $number }} protocol: TCP {{- end }} selector: {{- include "nifi.selectorLabels" . | nindent 4 }}