A "barcodes:" key left without children (e.g. after deleting the last remaining sub-key, PAPERLESS_CONSUMER_BARCODE_SCANNER, which paperless-ngx 3.0 removed) is YAML null and replaced the whole default sub-tree on merge. Rendering then failed on the nil pointer, so the release simply stopped updating. An empty or null block now means "the chart defaults apply". The merge runs through a new paperless-ngx.defaultedDict helper: nulls never delete, nested maps merge recursively, and explicitly set values win - including false and 0, which "default" and "mergeOverwrite" would swallow. A scalar where the defaults define a map is rejected with the values path in the message. Major bump: the meaning of a values override changes. Fixes #61
250 lines
11 KiB
YAML
250 lines
11 KiB
YAML
{{- /*
|
|
Barcode settings, defended against a null / empty "barcodes:" override:
|
|
an empty block falls back to the chart defaults (issue #61).
|
|
*/ -}}
|
|
{{- $barcodes := fromYaml (include "paperless-ngx.barcodes" .Values.paperless.files.barcodes) -}}
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
labels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
spec:
|
|
replicas: {{ include "paperless-ngx.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.paperless.replicas) }}
|
|
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
|
selector:
|
|
matchLabels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
template:
|
|
metadata:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
labels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
{{- toYaml .Values.paperless.securityContext.pod | nindent 8 }}
|
|
initContainers:
|
|
# s6-overlay refuses to start unless /run is owned by the runtime uid,
|
|
# but emptyDirs are always created root-owned. This minimal init
|
|
# container hands /run over to the paperless uid (see
|
|
# paperless.securityContext in values.yaml) so the main container can
|
|
# run fully hardened (non-root, no privilege escalation).
|
|
- name: init-run-ownership
|
|
image: "{{ .Values.paperless.busybox.image.repository }}:{{ .Values.paperless.busybox.image.tag }}"
|
|
imagePullPolicy: IfNotPresent
|
|
command:
|
|
- sh
|
|
- -c
|
|
- chown {{ .Values.paperless.securityContext.pod.runAsUser }}:{{ .Values.paperless.securityContext.pod.runAsGroup }} /run
|
|
securityContext:
|
|
runAsNonRoot: false
|
|
runAsUser: 0
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
add:
|
|
- CHOWN
|
|
volumeMounts:
|
|
- mountPath: /run
|
|
name: run
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 16Mi
|
|
ephemeral-storage: 1Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 32Mi
|
|
ephemeral-storage: 10Mi
|
|
containers:
|
|
- name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
image: "ghcr.io/paperless-ngx/paperless-ngx:{{ .Chart.AppVersion }}"
|
|
imagePullPolicy: IfNotPresent
|
|
securityContext:
|
|
{{- toYaml .Values.paperless.securityContext.container | nindent 12 }}
|
|
ports:
|
|
- containerPort: 8000
|
|
protocol: TCP
|
|
name: paperless-port
|
|
env:
|
|
- name: PAPERLESS_REDIS
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret
|
|
key: redis-connection-url
|
|
- name: PAPERLESS_REDIS_PREFIX
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret
|
|
key: key-prefix
|
|
- name: PAPERLESS_DBENGINE
|
|
value: "postgresql"
|
|
- name: PAPERLESS_DBHOST
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: server
|
|
- name: PAPERLESS_DBPORT
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: port
|
|
- name: PAPERLESS_DBNAME
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: database
|
|
- name: PAPERLESS_DBUSER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: username
|
|
- name: PAPERLESS_DBPASS
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: password
|
|
- name: PAPERLESS_ADMIN_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: username
|
|
- name: PAPERLESS_ADMIN_MAIL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: username
|
|
- name: PAPERLESS_ADMIN_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: password
|
|
- name: PAPERLESS_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: paperless-secret-key
|
|
- name: PAPERLESS_OCR_LANGUAGE
|
|
value: "{{ .Values.paperless.ocr.languages }}"
|
|
- name: PAPERLESS_OCR_MODE
|
|
value: "{{ .Values.paperless.ocr.mode }}"
|
|
- name: PAPERLESS_ARCHIVE_FILE_GENERATION
|
|
value: "{{ .Values.paperless.ocr.archiveFileGeneration }}"
|
|
- name: PAPERLESS_OCR_ROTATE_PAGES
|
|
value: "{{ .Values.paperless.ocr.rotate_pages }}"
|
|
- name: PAPERLESS_OCR_OUTPUT_TYPE
|
|
value: "{{ .Values.paperless.ocr.output_type }}"
|
|
- name: PAPERLESS_FILENAME_FORMAT
|
|
value: "{{ .Values.paperless.files.filenameFormat }}"
|
|
- name: PAPERLESS_URL
|
|
value: "https://{{ .Values.ingress.domain }}"
|
|
- name: PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE
|
|
value: "{{ $barcodes.asn.enabled }}"
|
|
- name: PAPERLESS_CONSUMER_ENABLE_BARCODES
|
|
value: "{{ $barcodes.enabled }}"
|
|
- name: PAPERLESS_CONSUMER_BARCODE_STRING
|
|
value: "{{ $barcodes.barcodeString }}"
|
|
- name: PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES
|
|
value: "{{ $barcodes.retainBarcodePage }}"
|
|
- name: PAPERLESS_CONSUMER_DELETE_DUPLICATES
|
|
value: "{{ .Values.paperless.files.deleteDuplicates }}"
|
|
{{- if .Values.paperless.files.tikaGotenberg.enabled }}
|
|
- name: PAPERLESS_TIKA_ENABLED
|
|
value: "{{ .Values.paperless.files.tikaGotenberg.enabled }}"
|
|
- name: PAPERLESS_TIKA_ENDPOINT
|
|
value: "http://{{ required "If Tika is enabled, a Tika-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.tikaEndpoint }}"
|
|
- name: PAPERLESS_TIKA_GOTENBERG_ENDPOINT
|
|
value: "http://{{ required "If Tika is enabled, a Gotenberg-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.gotenbergEndpoint }}"
|
|
{{- end }}
|
|
{{- if .Values.oidc.enabled }}
|
|
- name: PAPERLESS_APPS
|
|
value: "allauth.socialaccount.providers.openid_connect"
|
|
- name: PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
|
value: |-
|
|
{
|
|
"openid_connect": {
|
|
"OAUTH_PKCE_ENABLED": true,
|
|
"APPS": [
|
|
{
|
|
"provider_id": "{{ required "A OIDC-provider-id must be provided" .Values.oidc.provider.id }}",
|
|
"name": "{{ required "A OIDC-provider-name must be provided" .Values.oidc.provider.name }}",
|
|
"client_id": "{{ required "A OIDC-client-id must be provided" .Values.oidc.provider.clientId }}",
|
|
"settings": {
|
|
"fetch_userinfo": true,
|
|
"oauth_pkce_enabled": true,
|
|
"server_url": "{{ required "A OIDC-discovery-document-url must be provided" .Values.oidc.provider.discoveryUrl }}",
|
|
{{- if .Values.oidc.provider.tokenAuthMethod }}
|
|
"token_auth_method": "{{ .Values.oidc.provider.tokenAuthMethod }}",
|
|
{{- end }}
|
|
"uid_field": "sub"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
- name: PAPERLESS_REDIRECT_LOGIN_TO_SSO
|
|
value: "{{ .Values.oidc.autoLoginWithProvider }}"
|
|
- name: PAPERLESS_DISABLE_REGULAR_LOGIN
|
|
value: "{{ .Values.oidc.disableRegularLogin }}"
|
|
{{- end }}
|
|
{{- range $value := .Values.paperless.env }}
|
|
{{- if $value.value }}
|
|
- name: {{ $value.name }}
|
|
value: {{ tpl $value.value $ }}
|
|
{{- else if $value.valueFrom }}
|
|
{{- if $value.valueFrom.secretKeyRef }}
|
|
- name: {{ $value.name }}
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ tpl $value.valueFrom.secretKeyRef.name $ }}
|
|
key: {{ tpl $value.valueFrom.secretKeyRef.key $ }}
|
|
{{- else if $value.valueFrom.configMapKeyRef }}
|
|
- name: {{ $value.name }}
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ }}
|
|
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
volumeMounts:
|
|
- mountPath: "/usr/src/paperless/data"
|
|
name: "paperless-volume"
|
|
subPath: data/
|
|
- mountPath: "/usr/src/paperless/media"
|
|
name: "paperless-volume"
|
|
subPath: media/
|
|
- mountPath: "/usr/src/paperless/export"
|
|
name: "paperless-volume"
|
|
subPath: export/
|
|
- mountPath: "/usr/src/paperless/consume"
|
|
name: "paperless-consume-volume"
|
|
# Writable emptyDirs so the root filesystem can stay read-only:
|
|
# s6-overlay needs /run, OCR/consumption uses /tmp for scratch files.
|
|
- mountPath: /tmp
|
|
name: tmp
|
|
- mountPath: /run
|
|
name: run
|
|
resources:
|
|
{{- include "paperless-ngx.resources" .Values.paperless.resources | nindent 12 }}
|
|
readinessProbe:
|
|
{{- toYaml .Values.paperless.readinessProbe | nindent 12 }}
|
|
livenessProbe:
|
|
{{- toYaml .Values.paperless.livenessProbe | nindent 12 }}
|
|
startupProbe:
|
|
{{- toYaml .Values.paperless.startupProbe | nindent 12 }}
|
|
restartPolicy: Always
|
|
volumes:
|
|
- name: tmp
|
|
emptyDir: {}
|
|
- name: run
|
|
emptyDir: {}
|
|
- name: paperless-volume
|
|
persistentVolumeClaim:
|
|
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-volume-claim
|
|
- name: paperless-consume-volume
|
|
persistentVolumeClaim:
|
|
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-consume-volume-claim |